From 414abb514f78f9d2237ed35d22aa06321d9712a6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 13 Oct 2002 00:47:51 -0200 Subject: o ipv4: convert /proc/net/arp to seq_file --- include/net/arp.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/net/arp.h b/include/net/arp.h index f65d245f42cc..c6f2056d193f 100644 --- a/include/net/arp.h +++ b/include/net/arp.h @@ -18,6 +18,7 @@ extern void arp_send(int type, int ptype, u32 dest_ip, extern int arp_bind_neighbour(struct dst_entry *dst); extern int arp_mc_map(u32 addr, u8 *haddr, struct net_device *dev, int dir); extern void arp_ifdown(struct net_device *dev); +extern unsigned arp_state_to_flags(struct neighbour *neigh); extern struct neigh_ops arp_broken_ops; -- cgit v1.2.3 From 5f2e4b03c1a3fed601ff6d4f7484709f4fe9b871 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 13 Oct 2002 16:14:27 -0200 Subject: o ipv4: convert /proc/net/route to seq_file --- include/net/ip_fib.h | 25 +++++++++--------- net/ipv4/fib_frontend.c | 68 ++++++++---------------------------------------- net/ipv4/fib_hash.c | 37 ++++++++------------------ net/ipv4/fib_semantics.c | 30 ++++++++++----------- net/ipv4/ip_proc.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 110 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 236641e5bc51..01ac6fdd2617 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -18,6 +18,7 @@ #include #include +#include struct kern_rta { @@ -128,8 +129,7 @@ struct fib_table int (*tb_dump)(struct fib_table *table, struct sk_buff *skb, struct netlink_callback *cb); int (*tb_flush)(struct fib_table *table); - int (*tb_get_info)(struct fib_table *table, char *buf, - int first, int count); + int (*tb_seq_show)(struct fib_table *table, struct seq_file *seq); void (*tb_select_default)(struct fib_table *table, const struct flowi *flp, struct fib_result *res); @@ -138,14 +138,14 @@ struct fib_table #ifndef CONFIG_IP_MULTIPLE_TABLES -extern struct fib_table *local_table; -extern struct fib_table *main_table; +extern struct fib_table *ip_fib_local_table; +extern struct fib_table *ip_fib_main_table; static inline struct fib_table *fib_get_table(int id) { if (id != RT_TABLE_LOCAL) - return main_table; - return local_table; + return ip_fib_main_table; + return ip_fib_local_table; } static inline struct fib_table *fib_new_table(int id) @@ -155,8 +155,8 @@ static inline struct fib_table *fib_new_table(int id) static inline int fib_lookup(const struct flowi *flp, struct fib_result *res) { - if (local_table->tb_lookup(local_table, flp, res) && - main_table->tb_lookup(main_table, flp, res)) + if (ip_fib_local_table->tb_lookup(ip_fib_local_table, flp, res) && + ip_fib_main_table->tb_lookup(ip_fib_main_table, flp, res)) return -ENETUNREACH; return 0; } @@ -164,12 +164,12 @@ static inline int fib_lookup(const struct flowi *flp, struct fib_result *res) static inline void fib_select_default(const struct flowi *flp, struct fib_result *res) { if (FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) - main_table->tb_select_default(main_table, flp, res); + ip_fib_main_table->tb_select_default(ip_fib_main_table, flp, res); } #else /* CONFIG_IP_MULTIPLE_TABLES */ -#define local_table (fib_tables[RT_TABLE_LOCAL]) -#define main_table (fib_tables[RT_TABLE_MAIN]) +#define ip_fib_local_table (fib_tables[RT_TABLE_LOCAL]) +#define ip_fib_main_table (fib_tables[RT_TABLE_MAIN]) extern struct fib_table * fib_tables[RT_TABLE_MAX+1]; extern int fib_lookup(const struct flowi *flp, struct fib_result *res); @@ -222,7 +222,8 @@ extern int fib_sync_down(u32 local, struct net_device *dev, int force); extern int fib_sync_up(struct net_device *dev); extern int fib_convert_rtentry(int cmd, struct nlmsghdr *nl, struct rtmsg *rtm, struct kern_rta *rta, struct rtentry *r); -extern void fib_node_get_info(int type, int dead, struct fib_info *fi, u32 prefix, u32 mask, char *buffer); +extern void fib_node_seq_show(struct seq_file *seq, int type, int dead, + struct fib_info *fi, u32 prefix, u32 mask); extern u32 __fib_res_prefsrc(struct fib_result *res); /* Exported by fib_hash.c */ diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 74905d6696f2..99883006c28d 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -51,8 +50,8 @@ #define RT_TABLE_MIN RT_TABLE_MAIN -struct fib_table *local_table; -struct fib_table *main_table; +struct fib_table *ip_fib_local_table; +struct fib_table *ip_fib_main_table; #else @@ -88,56 +87,14 @@ void fib_flush(void) flushed += tb->tb_flush(tb); } #else /* CONFIG_IP_MULTIPLE_TABLES */ - flushed += main_table->tb_flush(main_table); - flushed += local_table->tb_flush(local_table); + flushed += ip_fib_main_table->tb_flush(ip_fib_main_table); + flushed += ip_fib_local_table->tb_flush(ip_fib_local_table); #endif /* CONFIG_IP_MULTIPLE_TABLES */ if (flushed) rt_cache_flush(-1); } - -#ifdef CONFIG_PROC_FS - -/* - * Called from the PROCfs module. This outputs /proc/net/route. - * - * It always works in backward compatibility mode. - * The format of the file is not supposed to be changed. - */ - -static int -fib_get_procinfo(char *buffer, char **start, off_t offset, int length) -{ - int first = offset/128; - char *ptr = buffer; - int count = (length+127)/128; - int len; - - *start = buffer + offset%128; - - if (--first < 0) { - sprintf(buffer, "%-127s\n", "Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT"); - --count; - ptr += 128; - first = 0; - } - - if (main_table && count > 0) { - int n = main_table->tb_get_info(main_table, ptr, first, count); - count -= n; - ptr += n*128; - } - len = ptr - *start; - if (len >= length) - return length; - if (len >= 0) - return len; - return 0; -} - -#endif /* CONFIG_PROC_FS */ - /* * Find the first device with a given source address. */ @@ -152,9 +109,9 @@ struct net_device * ip_dev_find(u32 addr) res.r = NULL; #endif - if (!local_table || local_table->tb_lookup(local_table, &fl, &res)) { + if (!ip_fib_local_table || + ip_fib_local_table->tb_lookup(ip_fib_local_table, &fl, &res)) return NULL; - } if (res.type != RTN_LOCAL) goto out; dev = FIB_RES_DEV(res); @@ -181,9 +138,10 @@ unsigned inet_addr_type(u32 addr) res.r = NULL; #endif - if (local_table) { + if (ip_fib_local_table) { ret = RTN_UNICAST; - if (local_table->tb_lookup(local_table, &fl, &res) == 0) { + if (!ip_fib_local_table->tb_lookup(ip_fib_local_table, + &fl, &res)) { ret = res.type; fib_res_put(&res); } @@ -636,13 +594,9 @@ struct notifier_block fib_netdev_notifier = { void __init ip_fib_init(void) { -#ifdef CONFIG_PROC_FS - proc_net_create("route",0,fib_get_procinfo); -#endif /* CONFIG_PROC_FS */ - #ifndef CONFIG_IP_MULTIPLE_TABLES - local_table = fib_hash_init(RT_TABLE_LOCAL); - main_table = fib_hash_init(RT_TABLE_MAIN); + ip_fib_local_table = fib_hash_init(RT_TABLE_LOCAL); + ip_fib_main_table = fib_hash_init(RT_TABLE_MAIN); #else fib_rules_init(); #endif diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c index 8b4ed3701998..ac266c630436 100644 --- a/net/ipv4/fib_hash.c +++ b/net/ipv4/fib_hash.c @@ -747,46 +747,31 @@ static int fn_hash_flush(struct fib_table *tb) #ifdef CONFIG_PROC_FS -static int fn_hash_get_info(struct fib_table *tb, char *buffer, int first, int count) +static int fn_hash_seq_show(struct fib_table *tb, struct seq_file *seq) { - struct fn_hash *table = (struct fn_hash*)tb->tb_data; + struct fn_hash *table = (struct fn_hash *)tb->tb_data; struct fn_zone *fz; - int pos = 0; - int n = 0; read_lock(&fib_hash_lock); - for (fz=table->fn_zone_list; fz; fz = fz->fz_next) { + for (fz = table->fn_zone_list; fz; fz = fz->fz_next) { int i; struct fib_node *f; int maxslot = fz->fz_divisor; struct fib_node **fp = fz->fz_hash; - if (fz->fz_nent == 0) + if (!fz->fz_nent) continue; - if (pos + fz->fz_nent <= first) { - pos += fz->fz_nent; - continue; - } - - for (i=0; i < maxslot; i++, fp++) { - for (f = *fp; f; f = f->fn_next) { - if (++pos <= first) - continue; - fib_node_get_info(f->fn_type, - f->fn_state&FN_S_ZOMBIE, + for (i = 0; i < maxslot; i++, fp++) + for (f = *fp; f; f = f->fn_next) + fib_node_seq_show(seq, f->fn_type, + f->fn_state & FN_S_ZOMBIE, FIB_INFO(f), fz_prefix(f->fn_key, fz), - FZ_MASK(fz), buffer); - buffer += 128; - if (++n >= count) - goto out; - } - } + FZ_MASK(fz)); } -out: read_unlock(&fib_hash_lock); - return n; + return 0; } #endif @@ -913,7 +898,7 @@ struct fib_table * __init fib_hash_init(int id) tb->tb_select_default = fn_hash_select_default; tb->tb_dump = fn_hash_dump; #ifdef CONFIG_PROC_FS - tb->tb_get_info = fn_hash_get_info; + tb->tb_seq_show = fn_hash_seq_show; #endif memset(tb->tb_data, 0, sizeof(struct fn_hash)); return tb; diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 4e2674a5ad79..bfb3e7da150d 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -1017,24 +1017,24 @@ static unsigned fib_flag_trans(int type, int dead, u32 mask, struct fib_info *fi return flags; } -void fib_node_get_info(int type, int dead, struct fib_info *fi, u32 prefix, u32 mask, char *buffer) +void fib_node_seq_show(struct seq_file *seq, int type, int dead, + struct fib_info *fi, u32 prefix, u32 mask) { - int len; + char bf[128]; unsigned flags = fib_flag_trans(type, dead, mask, fi); - if (fi) { - len = sprintf(buffer, "%s\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u", - fi->fib_dev ? fi->fib_dev->name : "*", prefix, - fi->fib_nh->nh_gw, flags, 0, 0, fi->fib_priority, - mask, fi->fib_advmss+40, fi->fib_window, fi->fib_rtt>>3); - } else { - len = sprintf(buffer, "*\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u", - prefix, 0, - flags, 0, 0, 0, - mask, 0, 0, 0); - } - memset(buffer+len, ' ', 127-len); - buffer[127] = '\n'; + if (fi) + snprintf(bf, sizeof(bf), + "%s\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u", + fi->fib_dev ? fi->fib_dev->name : "*", prefix, + fi->fib_nh->nh_gw, flags, 0, 0, fi->fib_priority, + mask, fi->fib_advmss + 40, fi->fib_window, + fi->fib_rtt >> 3); + else + snprintf(bf, sizeof(bf), + "*\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u", + prefix, 0, flags, 0, 0, 0, mask, 0, 0, 0); + seq_printf(seq, "%-127s\n", bf); } #endif diff --git a/net/ipv4/ip_proc.c b/net/ipv4/ip_proc.c index 7f6c3261317d..e7f7cf2c97bb 100644 --- a/net/ipv4/ip_proc.c +++ b/net/ipv4/ip_proc.c @@ -18,6 +18,9 @@ #include #include #include +#include +#include +#include #include #include @@ -30,6 +33,8 @@ extern int udp_get_info(char *, char **, off_t, int); #ifdef CONFIG_PROC_FS #ifdef CONFIG_AX25 + +/* ------------------------------------------------------------------------ */ /* * ax25 -> ASCII conversion */ @@ -159,6 +164,40 @@ static int arp_seq_show(struct seq_file *seq, void *v) return 0; } +/* ------------------------------------------------------------------------ */ + +static void *fib_seq_start(struct seq_file *seq, loff_t *pos) +{ + return *pos ? NULL : (void *)1; +} + +static void *fib_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + return NULL; +} + +static void fib_seq_stop(struct seq_file *seq, void *v) +{ +} +/* + * This outputs /proc/net/route. + * + * It always works in backward compatibility mode. + * The format of the file is not supposed to be changed. + */ +static int fib_seq_show(struct seq_file *seq, void *v) +{ + seq_printf(seq, "%-127s\n", "Iface\tDestination\tGateway " + "\tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU" + "\tWindow\tIRTT"); + if (ip_fib_main_table) + ip_fib_main_table->tb_seq_show(ip_fib_main_table, seq); + + return 0; +} + +/* ------------------------------------------------------------------------ */ + struct seq_operations arp_seq_ops = { .start = arp_seq_start, .next = arp_seq_next, @@ -166,11 +205,23 @@ struct seq_operations arp_seq_ops = { .show = arp_seq_show, }; +struct seq_operations fib_seq_ops = { + .start = fib_seq_start, + .next = fib_seq_next, + .stop = fib_seq_stop, + .show = fib_seq_show, +}; + static int arp_seq_open(struct inode *inode, struct file *file) { return seq_open(file, &arp_seq_ops); } +static int fib_seq_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &fib_seq_ops); +} + static struct file_operations arp_seq_fops = { .open = arp_seq_open, .read = seq_read, @@ -178,6 +229,15 @@ static struct file_operations arp_seq_fops = { .release = seq_release, }; +static struct file_operations fib_seq_fops = { + .open = fib_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +/* ------------------------------------------------------------------------ */ + int __init ipv4_proc_init(void) { struct proc_dir_entry *p; @@ -205,8 +265,15 @@ int __init ipv4_proc_init(void) if (!p) goto out_arp; p->proc_fops = &arp_seq_fops; + + p = create_proc_entry("route", S_IRUGO, proc_net); + if (!p) + goto out_route; + p->proc_fops = &fib_seq_fops; out: return rc; +out_route: + remove_proc_entry("route", proc_net); out_arp: proc_net_remove("udp"); out_udp: -- cgit v1.2.3 From a69156f1e1d80a36ff37e21a65f12acf77d40b72 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 13 Oct 2002 21:00:23 +0200 Subject: ALSA update - fixes for compilation without CONFIG_PROC_FS --- include/sound/core.h | 1 - include/sound/info.h | 6 +++--- sound/core/hwdep.c | 4 ++-- sound/core/info_oss.c | 2 +- sound/core/init.c | 2 +- sound/core/rawmidi.c | 4 ++-- sound/core/sound.c | 11 +++-------- sound/core/sound_oss.c | 12 ++++++++---- sound/core/timer.c | 4 ++-- 9 files changed, 22 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/sound/core.h b/include/sound/core.h index 8d90674f046c..a0051adef639 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -225,7 +225,6 @@ int snd_minor_info_oss_init(void); int snd_minor_info_oss_done(void); int snd_oss_init_module(void); -void snd_oss_cleanup_module(void); #endif diff --git a/include/sound/info.h b/include/sound/info.h index 576cc37de253..b45b422063e2 100644 --- a/include/sound/info.h +++ b/include/sound/info.h @@ -164,7 +164,7 @@ static inline int snd_info_card_unregister(snd_card_t * card) { return 0; } static inline int snd_info_register(snd_info_entry_t * entry) { return 0; } static inline int snd_info_unregister(snd_info_entry_t * entry) { return 0; } -static inline struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent) { return 0; } +static inline struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent) { return NULL; } static inline void snd_remove_proc_entry(struct proc_dir_entry *parent, struct proc_dir_entry *de) { ; } @@ -174,7 +174,7 @@ static inline void snd_remove_proc_entry(struct proc_dir_entry *parent, * OSS info part */ -#ifdef CONFIG_SND_OSSEMUL +#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS) #define SNDRV_OSS_INFO_DEV_AUDIO 0 #define SNDRV_OSS_INFO_DEV_SYNTH 1 @@ -187,6 +187,6 @@ static inline void snd_remove_proc_entry(struct proc_dir_entry *parent, extern int snd_oss_info_register(int dev, int num, char *string); #define snd_oss_info_unregister(dev, num) snd_oss_info_register(dev, num, NULL) -#endif /* CONFIG_SND_OSSEMUL */ +#endif /* CONFIG_SND_OSSEMUL && CONFIG_PROC_FS */ #endif /* __SOUND_INFO_H */ diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index 36a217c48e2c..92204af2f572 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -327,7 +327,7 @@ static int snd_hwdep_dev_register(snd_device_t *device) up(®ister_mutex); return err; } -#ifdef CONFIG_SND_OSSEMUL +#ifdef SNDRV_OSS_INFO_DEV_AUDIO hwdep->ossreg = 0; if (hwdep->oss_type >= 0) { if ((hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM) && (hwdep->device != 0)) { @@ -359,7 +359,7 @@ static int snd_hwdep_dev_unregister(snd_device_t *device) up(®ister_mutex); return -EINVAL; } -#ifdef CONFIG_SND_OSSEMUL +#ifdef SNDRV_OSS_INFO_DEV_AUDIO if (hwdep->ossreg) snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device); #endif diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c index 43bc8c56de01..d19840b4f0ce 100644 --- a/sound/core/info_oss.c +++ b/sound/core/info_oss.c @@ -28,7 +28,7 @@ #include #include -#ifdef CONFIG_SND_OSSEMUL +#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS) /* * OSS compatible part diff --git a/sound/core/init.c b/sound/core/init.c index c1099a1711e0..3e15f1a4fe61 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -216,7 +216,7 @@ static void snd_card_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buff snd_iprintf(buffer, "--- no soundcards ---\n"); } -#ifdef CONFIG_SND_OSSEMUL +#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS) void snd_card_info_read_oss(snd_info_buffer_t * buffer) { diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 88e62bc35846..f941791d14b1 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -1417,7 +1417,7 @@ static int snd_rawmidi_dev_register(snd_device_t *device) up(®ister_mutex); return err; } -#ifdef CONFIG_SND_OSSEMUL +#ifdef SNDRV_OSS_INFO_DEV_AUDIO rmidi->ossreg = 0; if (rmidi->device == snd_midi_map[rmidi->card->number]) { if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, @@ -1480,7 +1480,7 @@ static int snd_rawmidi_dev_unregister(snd_device_t *device) snd_info_unregister(rmidi->proc_entry); rmidi->proc_entry = NULL; } -#ifdef CONFIG_SND_OSSEMUL +#ifdef SNDRV_OSS_INFO_DEV_AUDIO if (rmidi->ossreg) { if (rmidi->device == snd_midi_map[rmidi->card->number]) { snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, rmidi->card, 0); diff --git a/sound/core/sound.c b/sound/core/sound.c index 6a7056336c57..b149ab583cdc 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -318,9 +318,6 @@ static int __init alsa_sound_init(void) #endif if (register_chrdev(snd_major, "alsa", &snd_fops)) { snd_printk(KERN_ERR "unable to register native major device number %d\n", snd_major); -#ifdef CONFIG_SND_OSSEMUL - snd_oss_cleanup_module(); -#endif return -EIO; } #ifdef CONFIG_SND_DEBUG_MEMORY @@ -329,9 +326,6 @@ static int __init alsa_sound_init(void) if (snd_info_init() < 0) { #ifdef CONFIG_SND_DEBUG_MEMORY snd_memory_done(); -#endif -#ifdef CONFIG_SND_OSSEMUL - snd_oss_cleanup_module(); #endif return -ENOMEM; } @@ -369,7 +363,6 @@ static void __exit alsa_sound_exit(void) #ifdef CONFIG_SND_OSSEMUL snd_info_minor_unregister(); - snd_oss_cleanup_module(); #endif snd_info_done(); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) && defined(CONFIG_APM) @@ -455,6 +448,7 @@ EXPORT_SYMBOL(snd_dma_disable); EXPORT_SYMBOL(snd_dma_residue); #endif /* info.c */ +#ifdef CONFIG_PROC_FS EXPORT_SYMBOL(snd_seq_root); EXPORT_SYMBOL(snd_create_proc_entry); EXPORT_SYMBOL(snd_remove_proc_entry); @@ -468,8 +462,9 @@ EXPORT_SYMBOL(snd_info_create_device); EXPORT_SYMBOL(snd_info_free_device); EXPORT_SYMBOL(snd_info_register); EXPORT_SYMBOL(snd_info_unregister); +#endif /* info_oss.c */ -#ifdef CONFIG_SND_OSSEMUL +#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS) EXPORT_SYMBOL(snd_oss_info_register); #endif /* control.c */ diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c index 348644a093b1..230f3cdcd0ee 100644 --- a/sound/core/sound_oss.c +++ b/sound/core/sound_oss.c @@ -183,6 +183,8 @@ int snd_unregister_oss_device(int type, snd_card_t * card, int dev) * INFO PART */ +#ifdef CONFIG_PROC_FS + static snd_info_entry_t *snd_minor_info_oss_entry = NULL; static void snd_minor_info_oss_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) @@ -207,8 +209,11 @@ static void snd_minor_info_oss_read(snd_info_entry_t *entry, snd_info_buffer_t * up(&sound_oss_mutex); } +#endif /* CONFIG_PROC_FS */ + int __init snd_minor_info_oss_init(void) { +#ifdef CONFIG_PROC_FS snd_info_entry_t *entry; entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root); @@ -222,13 +227,16 @@ int __init snd_minor_info_oss_init(void) } } snd_minor_info_oss_entry = entry; +#endif return 0; } int __exit snd_minor_info_oss_done(void) { +#ifdef CONFIG_PROC_FS if (snd_minor_info_oss_entry) snd_info_unregister(snd_minor_info_oss_entry); +#endif return 0; } @@ -241,8 +249,4 @@ int __init snd_oss_init_module(void) return 0; } -void snd_oss_cleanup_module(void) -{ -} - #endif /* CONFIG_SND_OSSEMUL */ diff --git a/sound/core/timer.c b/sound/core/timer.c index bd2c089ac19e..0c3b87e95746 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1356,7 +1356,7 @@ static int __init alsa_timer_init(void) int err; snd_info_entry_t *entry; -#ifdef CONFIG_SND_OSSEMUL +#ifdef SNDRV_OSS_INFO_DEV_TIMERS snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1, "system timer"); #endif if ((entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL)) != NULL) { @@ -1391,7 +1391,7 @@ static void __exit alsa_timer_exit(void) snd_info_unregister(snd_timer_proc_entry); snd_timer_proc_entry = NULL; } -#ifdef CONFIG_SND_OSSEMUL +#ifdef SNDRV_OSS_INFO_DEV_TIMERS snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1); #endif } -- cgit v1.2.3 From 13e242be9115e6a4909532d9c5b651a1d8c11493 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 13 Oct 2002 21:14:06 +0200 Subject: ALSA update - added SNDRV_PCM_IOCTL_HWSYNC ioctl - changed behaviour of read/write/poll functions - prefer waiting than return an error code - removed snd_seq_sleep_timeout_in_lock and snd_seq_sleep_in_lock helpers --- include/sound/asound.h | 3 +- sound/core/pcm_lib.c | 38 ++++++---------- sound/core/pcm_native.c | 91 ++++++++++++++++++------------------- sound/core/seq/oss/seq_oss_readq.c | 7 +-- sound/core/seq/oss/seq_oss_writeq.c | 4 +- sound/core/seq/seq.c | 4 +- sound/core/seq/seq_fifo.c | 4 +- sound/core/seq/seq_lock.c | 2 + sound/core/seq/seq_lock.h | 9 ---- sound/core/seq/seq_memory.c | 12 +++-- 10 files changed, 84 insertions(+), 90 deletions(-) (limited to 'include') diff --git a/include/sound/asound.h b/include/sound/asound.h index d749d968b9fb..9e351ebaf847 100644 --- a/include/sound/asound.h +++ b/include/sound/asound.h @@ -129,7 +129,7 @@ enum { * * *****************************************************************************/ -#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 2) +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 3) typedef unsigned long sndrv_pcm_uframes_t; typedef long sndrv_pcm_sframes_t; @@ -423,6 +423,7 @@ enum { SNDRV_PCM_IOCTL_SW_PARAMS = _IOWR('A', 0x13, struct sndrv_pcm_sw_params), SNDRV_PCM_IOCTL_STATUS = _IOR('A', 0x20, struct sndrv_pcm_status), SNDRV_PCM_IOCTL_DELAY = _IOR('A', 0x21, sndrv_pcm_sframes_t), + SNDRV_PCM_IOCTL_HWSYNC = _IO('A', 0x22), SNDRV_PCM_IOCTL_CHANNEL_INFO = _IOR('A', 0x32, struct sndrv_pcm_channel_info), SNDRV_PCM_IOCTL_PREPARE = _IO('A', 0x40), SNDRV_PCM_IOCTL_RESET = _IO('A', 0x41), diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index e26584646fd6..809d62c24d5b 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1854,16 +1854,11 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(snd_pcm_substream_t *substream, if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING) snd_pcm_update_hw_ptr(substream); avail = snd_pcm_playback_avail(runtime); - if (runtime->status->state == SNDRV_PCM_STATE_PAUSED || - runtime->status->state == SNDRV_PCM_STATE_PREPARED) { - if (avail < runtime->xfer_align) { - err = -EPIPE; - goto _end_unlock; - } - } else if (((avail < runtime->control->avail_min && size > avail) || - (size >= runtime->xfer_align && avail < runtime->xfer_align))) { + if (((avail < runtime->control->avail_min && size > avail) || + (size >= runtime->xfer_align && avail < runtime->xfer_align))) { wait_queue_t wait; enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED } state; + if (nonblock) { err = -EAGAIN; goto _end_unlock; @@ -1880,8 +1875,11 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(snd_pcm_substream_t *substream, spin_unlock_irq(&runtime->lock); if (schedule_timeout(10 * HZ) == 0) { spin_lock_irq(&runtime->lock); - state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; - break; + if (runtime->status->state != SNDRV_PCM_STATE_PREPARED && + runtime->status->state != SNDRV_PCM_STATE_PAUSED) { + state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; + break; + } } spin_lock_irq(&runtime->lock); switch (runtime->status->state) { @@ -1928,10 +1926,6 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(snd_pcm_substream_t *substream, cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; if (frames > cont) frames = cont; - if (frames == 0 && runtime->status->state == SNDRV_PCM_STATE_PAUSED) { - err = -EPIPE; - goto _end_unlock; - } snd_assert(frames != 0, spin_unlock_irq(&runtime->lock); return -EINVAL); @@ -2147,21 +2141,16 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(snd_pcm_substream_t *substream, void if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING) snd_pcm_update_hw_ptr(substream); avail = snd_pcm_capture_avail(runtime); - if (runtime->status->state == SNDRV_PCM_STATE_PAUSED) { + if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { if (avail < runtime->xfer_align) { err = -EPIPE; goto _end_unlock; } - } else if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { - if (avail < runtime->xfer_align) { - runtime->status->state = SNDRV_PCM_STATE_SETUP; - err = -EPIPE; - goto _end_unlock; - } } else if ((avail < runtime->control->avail_min && size > avail) || (size >= runtime->xfer_align && avail < runtime->xfer_align)) { wait_queue_t wait; enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED } state; + if (nonblock) { err = -EAGAIN; goto _end_unlock; @@ -2178,8 +2167,11 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(snd_pcm_substream_t *substream, void spin_unlock_irq(&runtime->lock); if (schedule_timeout(10 * HZ) == 0) { spin_lock_irq(&runtime->lock); - state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; - break; + if (runtime->status->state != SNDRV_PCM_STATE_PREPARED && + runtime->status->state != SNDRV_PCM_STATE_PAUSED) { + state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; + break; + } } spin_lock_irq(&runtime->lock); switch (runtime->status->state) { diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 0bb74778f82e..f0071e49df94 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1962,34 +1962,29 @@ snd_pcm_sframes_t snd_pcm_capture_rewind(snd_pcm_substream_t *substream, snd_pcm return ret; } -static int snd_pcm_playback_delay(snd_pcm_substream_t *substream, snd_pcm_sframes_t *res) +static int snd_pcm_hwsync(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime = substream->runtime; - int err = 0; - snd_pcm_sframes_t n; + int err; + spin_lock_irq(&runtime->lock); switch (runtime->status->state) { - case SNDRV_PCM_STATE_RUNNING: case SNDRV_PCM_STATE_DRAINING: - if (snd_pcm_update_hw_ptr(substream) >= 0) { - n = snd_pcm_playback_hw_avail(runtime); - if (put_user(n, res)) - err = -EFAULT; + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + goto __badfd; + case SNDRV_PCM_STATE_RUNNING: + if ((err = snd_pcm_update_hw_ptr(substream)) < 0) break; - } else { - err = SNDRV_PCM_STATE_RUNNING ? -EPIPE : -EBADFD; - } - break; + /* Fall through */ + case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_SUSPENDED: - if (runtime->status->suspended_state == SNDRV_PCM_STATE_RUNNING) { - n = snd_pcm_playback_hw_avail(runtime); - if (put_user(n, res)) - err = -EFAULT; - } else { - err = -EBADFD; - } + err = 0; + break; + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; break; default: + __badfd: err = -EBADFD; break; } @@ -1997,41 +1992,43 @@ static int snd_pcm_playback_delay(snd_pcm_substream_t *substream, snd_pcm_sframe return err; } -static int snd_pcm_capture_delay(snd_pcm_substream_t *substream, snd_pcm_sframes_t *res) +static int snd_pcm_delay(snd_pcm_substream_t *substream, snd_pcm_sframes_t *res) { snd_pcm_runtime_t *runtime = substream->runtime; - int err = 0; + int err; snd_pcm_sframes_t n; + spin_lock_irq(&runtime->lock); switch (runtime->status->state) { + case SNDRV_PCM_STATE_DRAINING: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + goto __badfd; case SNDRV_PCM_STATE_RUNNING: - if (snd_pcm_update_hw_ptr(substream) >= 0) { - n = snd_pcm_capture_avail(runtime); - if (put_user(n, res)) - err = -EFAULT; + if ((err = snd_pcm_update_hw_ptr(substream)) < 0) break; - } /* Fall through */ - case SNDRV_PCM_STATE_XRUN: - err = -EPIPE; - break; + case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_SUSPENDED: - if (runtime->status->suspended_state == SNDRV_PCM_STATE_RUNNING) { + err = 0; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + n = snd_pcm_playback_hw_avail(runtime); + else n = snd_pcm_capture_avail(runtime); - if (put_user(n, res)) - err = -EFAULT; - } else { - err = -EBADFD; - } + if (put_user(n, res)) + err = -EFAULT; + break; + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; break; default: + __badfd: err = -EBADFD; break; } spin_unlock_irq(&runtime->lock); return err; } - + static int snd_pcm_playback_ioctl1(snd_pcm_substream_t *substream, unsigned int cmd, void *arg); static int snd_pcm_capture_ioctl1(snd_pcm_substream_t *substream, @@ -2079,6 +2076,10 @@ static int snd_pcm_common_ioctl1(snd_pcm_substream_t *substream, return snd_pcm_resume(substream); case SNDRV_PCM_IOCTL_XRUN: return snd_pcm_xrun(substream); + case SNDRV_PCM_IOCTL_HWSYNC: + return snd_pcm_hwsync(substream); + case SNDRV_PCM_IOCTL_DELAY: + return snd_pcm_delay(substream, (snd_pcm_sframes_t *) arg); case SNDRV_PCM_IOCTL_HW_REFINE_OLD: return snd_pcm_hw_refine_old_user(substream, (struct sndrv_pcm_hw_params_old *) arg); case SNDRV_PCM_IOCTL_HW_PARAMS_OLD: @@ -2159,8 +2160,6 @@ static int snd_pcm_playback_ioctl1(snd_pcm_substream_t *substream, return snd_pcm_playback_drain(substream); case SNDRV_PCM_IOCTL_DROP: return snd_pcm_playback_drop(substream); - case SNDRV_PCM_IOCTL_DELAY: - return snd_pcm_playback_delay(substream, (snd_pcm_sframes_t*) arg); } return snd_pcm_common_ioctl1(substream, cmd, arg); } @@ -2228,8 +2227,6 @@ static int snd_pcm_capture_ioctl1(snd_pcm_substream_t *substream, return snd_pcm_capture_drain(substream); case SNDRV_PCM_IOCTL_DROP: return snd_pcm_capture_drop(substream); - case SNDRV_PCM_IOCTL_DELAY: - return snd_pcm_capture_delay(substream, (snd_pcm_sframes_t*) arg); } return snd_pcm_common_ioctl1(substream, cmd, arg); } @@ -2326,7 +2323,9 @@ static ssize_t snd_pcm_write(struct file *file, const char *buf, size_t count, l snd_pcm_runtime_t *runtime; snd_pcm_sframes_t result; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0) up(&file->f_dentry->d_inode->i_sem); +#endif pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, result = -ENXIO; goto end); substream = pcm_file->substream; snd_assert(substream != NULL, result = -ENXIO; goto end); @@ -2344,7 +2343,9 @@ static ssize_t snd_pcm_write(struct file *file, const char *buf, size_t count, l if (result > 0) result = frames_to_bytes(runtime, result); end: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0) down(&file->f_dentry->d_inode->i_sem); +#endif return result; } @@ -2449,6 +2450,8 @@ unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait) avail = snd_pcm_playback_avail(runtime); switch (runtime->status->state) { case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_PAUSED: if (avail >= runtime->control->avail_min) { mask = POLLOUT | POLLWRNORM; break; @@ -2457,12 +2460,6 @@ unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait) case SNDRV_PCM_STATE_DRAINING: mask = 0; break; - case SNDRV_PCM_STATE_PREPARED: - if (avail > 0) { - mask = POLLOUT | POLLWRNORM; - break; - } - /* Fall through */ default: mask = POLLOUT | POLLWRNORM | POLLERR; break; @@ -2491,6 +2488,8 @@ unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait) avail = snd_pcm_capture_avail(runtime); switch (runtime->status->state) { case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_PAUSED: if (avail >= runtime->control->avail_min) { mask = POLLIN | POLLRDNORM; break; diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c index 53ea18b72351..9690f8009bc1 100644 --- a/sound/core/seq/oss/seq_oss_readq.c +++ b/sound/core/seq/oss/seq_oss_readq.c @@ -159,9 +159,10 @@ snd_seq_oss_readq_pick(seq_oss_readq_t *q, int blocking, unsigned long *rflags) spin_lock_irqsave(&q->lock, *rflags); if (q->qlen == 0) { if (blocking) { - snd_seq_sleep_timeout_in_lock(&q->midi_sleep, - &q->lock, - q->pre_event_timeout); + spin_unlock(&q->lock); + interruptible_sleep_on_timeout(&q->midi_sleep, + q->pre_event_timeout); + spin_lock(&q->lock); } if (q->qlen == 0) { spin_unlock_irqrestore(&q->lock, *rflags); diff --git a/sound/core/seq/oss/seq_oss_writeq.c b/sound/core/seq/oss/seq_oss_writeq.c index 5d08882133b9..9f8a5438d056 100644 --- a/sound/core/seq/oss/seq_oss_writeq.c +++ b/sound/core/seq/oss/seq_oss_writeq.c @@ -122,7 +122,9 @@ snd_seq_oss_writeq_sync(seq_oss_writeq_t *q) } /* wait for echo event */ - snd_seq_sleep_timeout_in_lock(&q->sync_sleep, &q->sync_lock, HZ); + spin_unlock(&q->sync_lock); + interruptible_sleep_on_timeout(&q->sync_sleep, HZ); + spin_lock(&q->sync_lock); if (signal_pending(current)) { /* interrupted - return 0 to finish sync */ q->sync_event_put = 0; diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c index 6122671ddaf8..b6fdc9db2a5c 100644 --- a/sound/core/seq/seq.c +++ b/sound/core/seq/seq.c @@ -135,7 +135,7 @@ EXPORT_SYMBOL(snd_seq_event_port_attach); EXPORT_SYMBOL(snd_seq_event_port_detach); /* seq_lock.c */ #if defined(__SMP__) || defined(CONFIG_SND_DEBUG) -EXPORT_SYMBOL(snd_seq_sleep_in_lock); -EXPORT_SYMBOL(snd_seq_sleep_timeout_in_lock); +/*EXPORT_SYMBOL(snd_seq_sleep_in_lock);*/ +/*EXPORT_SYMBOL(snd_seq_sleep_timeout_in_lock);*/ EXPORT_SYMBOL(snd_use_lock_sync_helper); #endif diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c index 399acb13f1c3..80e4545e117c 100644 --- a/sound/core/seq/seq_fifo.c +++ b/sound/core/seq/seq_fifo.c @@ -182,7 +182,9 @@ int snd_seq_fifo_cell_out(fifo_t *f, snd_seq_event_cell_t **cellp, int nonblock) spin_unlock_irqrestore(&f->lock, flags); return -EAGAIN; } - snd_seq_sleep_in_lock(&f->input_sleep, &f->lock); + spin_unlock(&f->lock); + interruptible_sleep_on(&f->input_sleep); + spin_lock(&f->lock); if (signal_pending(current)) { spin_unlock_irqrestore(&f->lock, flags); diff --git a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c index 8ad28eb9ccd1..289393276663 100644 --- a/sound/core/seq/seq_lock.c +++ b/sound/core/seq/seq_lock.c @@ -25,6 +25,7 @@ #if defined(__SMP__) || defined(CONFIG_SND_DEBUG) +#if 0 /* NOT USED */ /* (interruptible) sleep_on during the specified spinlock */ void snd_seq_sleep_in_lock(wait_queue_head_t *p, spinlock_t *lock) { @@ -60,6 +61,7 @@ long snd_seq_sleep_timeout_in_lock(wait_queue_head_t *p, spinlock_t *lock, long return timeout; } +#endif /* NOT USED */ /* wait until all locks are released */ void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line) diff --git a/sound/core/seq/seq_lock.h b/sound/core/seq/seq_lock.h index 79080cebc289..68b3271cdfc7 100644 --- a/sound/core/seq/seq_lock.h +++ b/sound/core/seq/seq_lock.h @@ -20,12 +20,6 @@ typedef atomic_t snd_use_lock_t; void snd_use_lock_sync_helper(snd_use_lock_t *lock, const char *file, int line); #define snd_use_lock_sync(lockp) snd_use_lock_sync_helper(lockp, __BASE_FILE__, __LINE__) -/* (interruptible) sleep_on during the specified spinlock */ -void snd_seq_sleep_in_lock(wait_queue_head_t *p, spinlock_t *lock); - -/* (interruptible) sleep_on with timeout during the specified spinlock */ -long snd_seq_sleep_timeout_in_lock(wait_queue_head_t *p, spinlock_t *lock, long timeout); - #else /* SMP || CONFIG_SND_DEBUG */ typedef spinlock_t snd_use_lock_t; /* dummy */ @@ -34,9 +28,6 @@ typedef spinlock_t snd_use_lock_t; /* dummy */ #define snd_use_lock_free(lockp) /**/ #define snd_use_lock_sync(lockp) /**/ -#define snd_seq_sleep_in_lock(p,lock) interruptible_sleep_on(p) -#define snd_seq_sleep_timeout_in_lock(p,lock,timeout) interruptible_sleep_on_timeout(p,timeout) - #endif /* SMP || CONFIG_SND_DEBUG */ #endif /* __SND_SEQ_LOCK_H */ diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index b577622c58ff..87aa5a6cc706 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -233,17 +233,21 @@ int snd_seq_cell_alloc(pool_t *pool, snd_seq_event_cell_t **cellp, int nonblock, goto __error; } while (pool->free == NULL && ! nonblock && ! pool->closing) { + + spin_unlock(&pool->lock); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0) /* change semaphore to allow other clients to access device file */ if (file) up(&semaphore_of(file)); - - snd_seq_sleep_in_lock(&pool->output_sleep, &pool->lock); - +#endif + interruptible_sleep_on(&pool->output_sleep); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0) /* restore semaphore again */ if (file) down(&semaphore_of(file)); - +#endif + spin_lock(&pool->lock); /* interrupted? */ if (signal_pending(current)) { err = -ERESTARTSYS; -- cgit v1.2.3 From 49daee21a02ddd4584ef2492e55b3ee31d1f487d Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 13 Oct 2002 14:51:42 -0700 Subject: [NETFILTER]: Avoid nesting readlocks in conntrack code. --- include/linux/netfilter_ipv4/ip_conntrack_core.h | 2 +- net/ipv4/netfilter/ip_conntrack_core.c | 4 ++-- net/ipv4/netfilter/ip_conntrack_standalone.c | 5 +++-- net/ipv4/netfilter/ip_nat_core.c | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter_ipv4/ip_conntrack_core.h b/include/linux/netfilter_ipv4/ip_conntrack_core.h index 7d8b393f3c44..c46f0e86fe60 100644 --- a/include/linux/netfilter_ipv4/ip_conntrack_core.h +++ b/include/linux/netfilter_ipv4/ip_conntrack_core.h @@ -17,7 +17,7 @@ extern void ip_conntrack_cleanup(void); struct ip_conntrack_protocol; extern struct ip_conntrack_protocol *ip_ct_find_proto(u_int8_t protocol); /* Like above, but you already have conntrack read lock. */ -extern struct ip_conntrack_protocol *__find_proto(u_int8_t protocol); +extern struct ip_conntrack_protocol *__ip_ct_find_proto(u_int8_t protocol); extern struct list_head protocol_list; /* Returns conntrack if it dealt with ICMP, and filled in skb->nfct */ diff --git a/net/ipv4/netfilter/ip_conntrack_core.c b/net/ipv4/netfilter/ip_conntrack_core.c index 1b576c42cc2f..712869863cd2 100644 --- a/net/ipv4/netfilter/ip_conntrack_core.c +++ b/net/ipv4/netfilter/ip_conntrack_core.c @@ -75,7 +75,7 @@ static inline int proto_cmpfn(const struct ip_conntrack_protocol *curr, return protocol == curr->proto; } -struct ip_conntrack_protocol *__find_proto(u_int8_t protocol) +struct ip_conntrack_protocol *__ip_ct_find_proto(u_int8_t protocol) { struct ip_conntrack_protocol *p; @@ -93,7 +93,7 @@ struct ip_conntrack_protocol *ip_ct_find_proto(u_int8_t protocol) struct ip_conntrack_protocol *p; READ_LOCK(&ip_conntrack_lock); - p = __find_proto(protocol); + p = __ip_ct_find_proto(protocol); READ_UNLOCK(&ip_conntrack_lock); return p; } diff --git a/net/ipv4/netfilter/ip_conntrack_standalone.c b/net/ipv4/netfilter/ip_conntrack_standalone.c index 092ea4c00f54..9cebe6d8501a 100644 --- a/net/ipv4/netfilter/ip_conntrack_standalone.c +++ b/net/ipv4/netfilter/ip_conntrack_standalone.c @@ -71,7 +71,7 @@ print_expect(char *buffer, const struct ip_conntrack_expect *expect) len += sprintf(buffer + len, "use=%u proto=%u ", atomic_read(&expect->use), expect->tuple.dst.protonum); len += print_tuple(buffer + len, &expect->tuple, - __find_proto(expect->tuple.dst.protonum)); + __ip_ct_find_proto(expect->tuple.dst.protonum)); len += sprintf(buffer + len, "\n"); return len; } @@ -81,7 +81,7 @@ print_conntrack(char *buffer, const struct ip_conntrack *conntrack) { unsigned int len; struct ip_conntrack_protocol *proto - = __find_proto(conntrack->tuplehash[IP_CT_DIR_ORIGINAL] + = __ip_ct_find_proto(conntrack->tuplehash[IP_CT_DIR_ORIGINAL] .tuple.dst.protonum); len = sprintf(buffer, "%-8s %u %lu ", @@ -361,6 +361,7 @@ EXPORT_SYMBOL(ip_conntrack_helper_unregister); EXPORT_SYMBOL(ip_ct_selective_cleanup); EXPORT_SYMBOL(ip_ct_refresh); EXPORT_SYMBOL(ip_ct_find_proto); +EXPORT_SYMBOL(__ip_ct_find_proto); EXPORT_SYMBOL(ip_ct_find_helper); EXPORT_SYMBOL(ip_conntrack_expect_related); EXPORT_SYMBOL(ip_conntrack_change_expect); diff --git a/net/ipv4/netfilter/ip_nat_core.c b/net/ipv4/netfilter/ip_nat_core.c index 38a8e37ffd9a..020a6af6acde 100644 --- a/net/ipv4/netfilter/ip_nat_core.c +++ b/net/ipv4/netfilter/ip_nat_core.c @@ -740,7 +740,7 @@ static inline int exp_for_packet(struct ip_conntrack_expect *exp, int ret = 1; MUST_BE_READ_LOCKED(&ip_conntrack_lock); - proto = ip_ct_find_proto((*pskb)->nh.iph->protocol); + proto = __ip_ct_find_proto((*pskb)->nh.iph->protocol); if (proto->exp_matches_pkt) ret = proto->exp_matches_pkt(exp, pskb); -- cgit v1.2.3 From f96f11d2fabe4e837ed147830a69c2725a4d64bc Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 14 Oct 2002 00:21:21 +0200 Subject: ALSA update - USB driver update - better extdigi support (mixer) - cleanups in the audio code --- include/sound/version.h | 2 +- sound/usb/usbaudio.c | 28 +++-- sound/usb/usbmixer.c | 271 ++++++++++++++++++++++++++++++++-------------- sound/usb/usbmixer_maps.c | 100 +++++++++++++++++ 4 files changed, 307 insertions(+), 94 deletions(-) create mode 100644 sound/usb/usbmixer_maps.c (limited to 'include') diff --git a/include/sound/version.h b/include/sound/version.h index ba48af7a3099..11fb715148ce 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h. Generated automatically by configure. */ #define CONFIG_SND_VERSION "0.9.0rc3" -#define CONFIG_SND_DATE " (Fri Oct 04 13:09:13 2002 UTC)" +#define CONFIG_SND_DATE " (Sun Oct 13 15:15:37 2002 UTC)" diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index a71a1586543e..8588a0d52b28 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -957,6 +957,19 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime) if (subs->interface >= 0 && subs->interface != fmt->iface) { usb_set_interface(subs->dev, subs->interface, 0); subs->interface = -1; + subs->format = 0; + } + + /* set interface */ + if (subs->interface != fmt->iface || subs->format != fmt->altset_idx) { + if (usb_set_interface(dev, fmt->iface, fmt->altset_idx) < 0) { + snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n", + dev->devnum, fmt->iface, fmt->altsetting); + return -EIO; + } + snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altset_idx); + subs->interface = fmt->iface; + subs->format = fmt->altset_idx; } /* create a data pipe */ @@ -965,7 +978,6 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime) subs->datapipe = usb_sndisocpipe(dev, ep); else subs->datapipe = usb_rcvisocpipe(dev, ep); - subs->format = fmt->altset_idx; subs->syncpipe = subs->syncinterval = 0; subs->maxpacksize = alts->endpoint[0].wMaxPacketSize; subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize); @@ -998,15 +1010,6 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime) subs->syncinterval = alts->endpoint[1].bRefresh; } - /* set interface */ - if (usb_set_interface(dev, fmt->iface, fmt->altset_idx) < 0) { - snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n", - dev->devnum, fmt->iface, fmt->altsetting); - return -EIO; - } - snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altset_idx); - subs->interface = fmt->iface; - ep = alts->endpoint[0].bEndpointAddress; /* if endpoint has pitch control, enable it */ if (fmt->attributes & EP_CS_ATTR_PITCH_CONTROL) { @@ -1171,6 +1174,7 @@ static int snd_usb_pcm_open(snd_pcm_substream_t *substream, int direction, snd_usb_substream_t *subs = &as->substream[direction]; subs->interface = -1; + subs->format = 0; runtime->hw = *hw; runtime->private_data = subs; subs->pcm_substream = substream; @@ -1601,6 +1605,10 @@ static int parse_audio_format_type(struct usb_device *dev, int iface_no, int alt /* FIXME: correct endianess and sign? */ pcm_format = -1; switch (format) { + case 0: /* some devices don't define this correctly... */ + snd_printd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n", + dev->devnum, iface_no, altno); + /* fall-through */ case USB_AUDIO_FORMAT_PCM: /* check the format byte size */ switch (fmt[6]) { diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index 04da43eb1bef..7be5bbc3e8e0 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c @@ -55,6 +55,8 @@ struct usb_audio_term { int name; }; +struct usbmix_name_map; + struct usb_mixer_build { snd_usb_audio_t *chip; unsigned char *buffer; @@ -62,6 +64,7 @@ struct usb_mixer_build { unsigned int ctrlif; DECLARE_BITMAP(unitbitmap, 32*32); usb_audio_term_t oterm; + const struct usbmix_name_map *map; }; struct usb_mixer_elem_info { @@ -78,7 +81,8 @@ struct usb_mixer_elem_info { enum { - USB_FEATURE_MUTE = 0, + USB_FEATURE_NONE = 0, + USB_FEATURE_MUTE = 1, USB_FEATURE_VOLUME, USB_FEATURE_BASS, USB_FEATURE_MID, @@ -99,9 +103,71 @@ enum { USB_MIXER_U16, }; +enum { + USB_PROC_UPDOWN = 1, + USB_PROC_UPDOWN_SWITCH = 1, + USB_PROC_UPDOWN_MODE_SEL = 2, + + USB_PROC_PROLOGIC = 2, + USB_PROC_PROLOGIC_SWITCH = 1, + USB_PROC_PROLOGIC_MODE_SEL = 2, + + USB_PROC_3DENH = 3, + USB_PROC_3DENH_SWITCH = 1, + USB_PROC_3DENH_SPACE = 2, + + USB_PROC_REVERB = 4, + USB_PROC_REVERB_SWITCH = 1, + USB_PROC_REVERB_LEVEL = 2, + USB_PROC_REVERB_TIME = 3, + USB_PROC_REVERB_DELAY = 4, + + USB_PROC_CHORUS = 5, + USB_PROC_CHORUS_SWITCH = 1, + USB_PROC_CHORUS_LEVEL = 2, + USB_PROC_CHORUS_RATE = 3, + USB_PROC_CHORUS_DEPTH = 4, + + USB_PROC_DCR = 6, + USB_PROC_DCR_SWITCH = 1, + USB_PROC_DCR_RATIO = 2, + USB_PROC_DCR_MAX_AMP = 3, + USB_PROC_DCR_THRESHOLD = 4, + USB_PROC_DCR_ATTACK = 5, + USB_PROC_DCR_RELEASE = 6, +}; + #define MAX_CHANNELS 10 /* max logical channels */ +/* + * manual mapping of mixer names + * if the mixer topology is too complicated and the parsed names are + * ambiguous, add the entries in usbmixer_maps.c. + */ +#include "usbmixer_maps.c" + +/* get the mapped name if the unit matches */ +static int check_mapped_name(mixer_build_t *state, int unitid, int control, char *buf, int buflen) +{ + const struct usbmix_name_map *p; + + if (! state->map) + return 0; + + for (p = state->map; p->id; p++) { + if (p->id == unitid && + (! control || ! p->control || control == p->control)) { + buflen--; + strncpy(buf, p->name, buflen); + buf[buflen] = 0; + return strlen(buf); + } + } + return 0; +} + + /* * find an audio control unit with the given unit id */ @@ -227,7 +293,7 @@ static int get_cur_ctl_value(usb_mixer_elem_info_t *cval, int validx, int *value /* channel = 0: master, 1 = first channel */ inline static int get_cur_mix_value(usb_mixer_elem_info_t *cval, int channel, int *value) { - return get_ctl_value(cval, GET_CUR, ((cval->control + 1) << 8) | channel, value); + return get_ctl_value(cval, GET_CUR, (cval->control << 8) | channel, value); } /* @@ -256,7 +322,7 @@ static int set_cur_ctl_value(usb_mixer_elem_info_t *cval, int validx, int value) inline static int set_cur_mix_value(usb_mixer_elem_info_t *cval, int channel, int value) { - return set_ctl_value(cval, SET_CUR, ((cval->control + 1) << 8) | channel, value); + return set_ctl_value(cval, SET_CUR, (cval->control << 8) | channel, value); } @@ -291,7 +357,7 @@ static int add_control_to_empty(snd_card_t *card, snd_kcontrol_t *kctl) while (snd_ctl_find_id(card, &kctl->id)) kctl->id.index++; if ((err = snd_ctl_add(card, kctl)) < 0) { - snd_printk(KERN_ERR "cannot add control\n"); + snd_printd(KERN_ERR "cannot add control (err = %d)\n", err); snd_ctl_free_one(kctl); } return err; @@ -509,9 +575,9 @@ static int mixer_ctl_feature_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t break; } } - if (get_ctl_value(cval, GET_MAX, ((cval->control+1) << 8) | minchn, &cval->max) < 0 || - get_ctl_value(cval, GET_MIN, ((cval->control+1) << 8) | minchn, &cval->min) < 0) { - snd_printk(KERN_ERR "%d:%d: cannot get min/max values for control %d\n", cval->id, cval->ctrlif, cval->control); + if (get_ctl_value(cval, GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 || + get_ctl_value(cval, GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) { + snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n", cval->id, cval->ctrlif, cval->control, cval->id); return -EINVAL; } cval->initialized = 1; @@ -534,7 +600,7 @@ static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t if (cval->cmask & (1 << c)) { err = get_cur_mix_value(cval, c + 1, &val); if (err < 0) { - printk("cannot get current value for control %d ch %d: err = %d\n", cval->control, c + 1, err); + snd_printd(KERN_ERR "cannot get current value for control %d ch %d: err = %d\n", cval->control, c + 1, err); return err; } val = get_relative_value(cval, val); @@ -546,7 +612,7 @@ static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t /* master channel */ err = get_cur_mix_value(cval, 0, &val); if (err < 0) { - printk("cannot get current value for control %d master ch: err = %d\n", cval->control, err); + snd_printd(KERN_ERR "cannot get current value for control %d master ch: err = %d\n", cval->control, err); return err; } val = get_relative_value(cval, val); @@ -610,12 +676,14 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, unsigned int ctl_mask, int control, usb_audio_term_t *iterm, int unitid) { - int len = 0; + int len = 0, mapped_name = 0; int nameid = desc[desc[0] - 1]; snd_kcontrol_t *kctl; usb_mixer_elem_info_t *cval; int minchn = 0; + control++; /* change from zero-based to 1-based value */ + if (control == USB_FEATURE_GEQ) { /* FIXME: not supported yet */ return; @@ -623,7 +691,7 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL); if (! cval) { - snd_printk(KERN_ERR "cannot malloc kcontrol"); + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); return; } cval->chip = state->chip; @@ -631,7 +699,7 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, cval->id = unitid; cval->control = control; cval->cmask = ctl_mask; - cval->val_type = audio_feature_info[control].type; + cval->val_type = audio_feature_info[control-1].type; if (ctl_mask == 0) cval->channels = 1; /* master channel */ else { @@ -651,22 +719,24 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, cval->max = 1; cval->initialized = 1; } else { - if (get_ctl_value(cval, GET_MAX, ((cval->control+1) << 8) | minchn, &cval->max) < 0 || - get_ctl_value(cval, GET_MIN, ((cval->control+1) << 8) | minchn, &cval->min) < 0) - snd_printk(KERN_ERR "%d:%d: cannot get min/max values for control %d\n", cval->id, cval->ctrlif, control); + if (get_ctl_value(cval, GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 || + get_ctl_value(cval, GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) + snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n", cval->id, cval->ctrlif, control, unitid); else cval->initialized = 1; } kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); if (! kctl) { - snd_printk(KERN_ERR "cannot malloc kcontrol"); + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); snd_magic_kfree(cval); return; } kctl->private_free = usb_mixer_elem_free; - if (nameid) + len = check_mapped_name(state, unitid, control, kctl->id.name, sizeof(kctl->id.name)); + mapped_name = len != 0; + if (! len && nameid) len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); switch (control) { @@ -679,7 +749,7 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, * - if the connected output can be determined, use it. * - otherwise, anonymous name. */ - if (! nameid) { + if (! len) { len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 1); if (! len) len = get_term_name(state, &state->oterm, kctl->id.name, sizeof(kctl->id.name), 1); @@ -690,21 +760,26 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, * if the connected output is USB stream, then it's likely a * capture stream. otherwise it should be playback (hopefully :) */ - if (! (state->oterm.type >> 16)) { + if (! mapped_name && ! (state->oterm.type >> 16)) { if ((state->oterm.type & 0xff00) == 0x0100) { - strcpy(kctl->id.name + len, " Capture"); - len += 8; + if (len + 8 < sizeof(kctl->id.name)) { + strcpy(kctl->id.name + len, " Capture"); + len += 8; + } } else { - strcpy(kctl->id.name + len, " Playback"); - len += 9; + if (len + 9 < sizeof(kctl->id.name)) { + strcpy(kctl->id.name + len, " Playback"); + len += 9; + } } } - strcpy(kctl->id.name + len, control == USB_FEATURE_MUTE ? " Switch" : " Volume"); + if (len + 7 < sizeof(kctl->id.name)) + strcpy(kctl->id.name + len, control == USB_FEATURE_MUTE ? " Switch" : " Volume"); break; default: - if (! nameid) - strcpy(kctl->id.name, audio_feature_info[control].name); + if (! len) + strcpy(kctl->id.name, audio_feature_info[control-1].name); break; } @@ -797,7 +872,7 @@ static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc, cval->chip = state->chip; cval->ctrlif = state->ctrlif; cval->id = unitid; - cval->control = in_ch; + cval->control = in_ch + 1; /* based on 1 */ cval->val_type = USB_MIXER_S16; for (i = 0; i < num_outs; i++) { if (check_matrix_bitmap(desc + 9 + num_ins, in_ch, i, num_outs)) { @@ -809,24 +884,27 @@ static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc, } /* get min/max values */ - if (get_ctl_value(cval, GET_MAX, ((in_ch+1) << 8) | minchn, &cval->max) < 0 || - get_ctl_value(cval, GET_MIN, ((in_ch+1) << 8) | minchn, &cval->min) < 0) - snd_printk(KERN_ERR "cannot get min/max values for mixer\n"); + if (get_ctl_value(cval, GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 || + get_ctl_value(cval, GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) + snd_printd(KERN_ERR "cannot get min/max values for mixer (id %d)\n", unitid); else cval->initialized = 1; kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); if (! kctl) { - snd_printk(KERN_ERR "cannot malloc kcontrol"); + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); snd_magic_kfree(cval); return; } kctl->private_free = usb_mixer_elem_free; - len = get_term_name(state, &iterm, kctl->id.name, sizeof(kctl->id.name), 0); + len = check_mapped_name(state, unitid, 0, kctl->id.name, sizeof(kctl->id.name)); + if (! len) + len = get_term_name(state, &iterm, kctl->id.name, sizeof(kctl->id.name), 0); if (! len) len = sprintf(kctl->id.name, "Mixer Source %d", in_ch); - strcpy(kctl->id.name + len, " Volume"); + if (len + 7 < sizeof(kctl->id.name)) + strcpy(kctl->id.name + len, " Volume"); snd_printdd(KERN_INFO "[%d] MU [%s] ch = %d, val = %d/%d\n", cval->id, kctl->id.name, cval->channels, cval->min, cval->max); @@ -917,51 +995,51 @@ struct procunit_info { }; static struct procunit_value_info updown_proc_info[] = { - { 0x01, "Switch", USB_MIXER_BOOLEAN }, - { 0x02, "Mode Select", USB_MIXER_U8 }, + { USB_PROC_UPDOWN_SWITCH, "Switch", USB_MIXER_BOOLEAN }, + { USB_PROC_UPDOWN_MODE_SEL, "Mode Select", USB_MIXER_U8 }, { 0 } }; static struct procunit_value_info prologic_proc_info[] = { - { 0x01, "Switch", USB_MIXER_BOOLEAN }, - { 0x02, "Mode Select", USB_MIXER_U8 }, + { USB_PROC_PROLOGIC_SWITCH, "Switch", USB_MIXER_BOOLEAN }, + { USB_PROC_PROLOGIC_MODE_SEL, "Mode Select", USB_MIXER_U8 }, { 0 } }; static struct procunit_value_info threed_enh_proc_info[] = { - { 0x01, "Switch", USB_MIXER_BOOLEAN }, - { 0x02, "Spaciousness", USB_MIXER_U8 }, + { USB_PROC_3DENH_SWITCH, "Switch", USB_MIXER_BOOLEAN }, + { USB_PROC_3DENH_SPACE, "Spaciousness", USB_MIXER_U8 }, { 0 } }; static struct procunit_value_info reverb_proc_info[] = { - { 0x01, "Switch", USB_MIXER_BOOLEAN }, - { 0x02, "Level", USB_MIXER_U8 }, - { 0x03, "Time", USB_MIXER_U16 }, - { 0x04, "Delay", USB_MIXER_U8 }, + { USB_PROC_REVERB_SWITCH, "Switch", USB_MIXER_BOOLEAN }, + { USB_PROC_REVERB_LEVEL, "Level", USB_MIXER_U8 }, + { USB_PROC_REVERB_TIME, "Time", USB_MIXER_U16 }, + { USB_PROC_REVERB_DELAY, "Delay", USB_MIXER_U8 }, { 0 } }; static struct procunit_value_info chorus_proc_info[] = { - { 0x01, "Switch", USB_MIXER_BOOLEAN }, - { 0x02, "Level", USB_MIXER_U8 }, - { 0x03, "Rate", USB_MIXER_U16 }, - { 0x04, "Depth", USB_MIXER_U16 }, + { USB_PROC_CHORUS_SWITCH, "Switch", USB_MIXER_BOOLEAN }, + { USB_PROC_CHORUS_LEVEL, "Level", USB_MIXER_U8 }, + { USB_PROC_CHORUS_RATE, "Rate", USB_MIXER_U16 }, + { USB_PROC_CHORUS_DEPTH, "Depth", USB_MIXER_U16 }, { 0 } }; static struct procunit_value_info dcr_proc_info[] = { - { 0x01, "Switch", USB_MIXER_BOOLEAN }, - { 0x02, "Ratio", USB_MIXER_U16 }, - { 0x03, "Max Amp", USB_MIXER_S16 }, - { 0x04, "Threshold", USB_MIXER_S16 }, - { 0x05, "Attack Time", USB_MIXER_U16 }, - { 0x06, "Release Time", USB_MIXER_U16 }, + { USB_PROC_DCR_SWITCH, "Switch", USB_MIXER_BOOLEAN }, + { USB_PROC_DCR_RATIO, "Ratio", USB_MIXER_U16 }, + { USB_PROC_DCR_MAX_AMP, "Max Amp", USB_MIXER_S16 }, + { USB_PROC_DCR_THRESHOLD, "Threshold", USB_MIXER_S16 }, + { USB_PROC_DCR_ATTACK, "Attack Time", USB_MIXER_U16 }, + { USB_PROC_DCR_RELEASE, "Release Time", USB_MIXER_U16 }, { 0 } }; static struct procunit_info procunits[] = { - { 0x01, "Up Down", updown_proc_info }, - { 0x02, "Dolby Prologic", prologic_proc_info }, - { 0x03, "3D Stereo Extender", threed_enh_proc_info }, - { 0x04, "Reverb", reverb_proc_info }, - { 0x05, "Chorus", chorus_proc_info }, - { 0x06, "DCR", dcr_proc_info }, + { USB_PROC_UPDOWN, "Up Down", updown_proc_info }, + { USB_PROC_PROLOGIC, "Dolby Prologic", prologic_proc_info }, + { USB_PROC_3DENH, "3D Stereo Extender", threed_enh_proc_info }, + { USB_PROC_REVERB, "Reverb", reverb_proc_info }, + { USB_PROC_CHORUS, "Chorus", chorus_proc_info }, + { USB_PROC_DCR, "DCR", dcr_proc_info }, { 0 }, }; @@ -973,7 +1051,7 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char int num_ins = dsc[6]; usb_mixer_elem_info_t *cval; snd_kcontrol_t *kctl; - int i, err, nameid, type; + int i, err, nameid, type, len; struct procunit_info *info; struct procunit_value_info *valinfo; static struct procunit_value_info default_value_info[] = { @@ -985,7 +1063,7 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char }; if (dsc[0] < 13 || dsc[0] < 13 + num_ins || dsc[0] < num_ins + dsc[11 + num_ins]) { - snd_printk(KERN_ERR "invalid %s descriptor %d\n", name, unitid); + snd_printk(KERN_ERR "invalid %s descriptor (id %d)\n", name, unitid); return -EINVAL; } @@ -1010,7 +1088,7 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char continue; cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL); if (! cval) { - snd_printk(KERN_ERR "cannot malloc kcontrol"); + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); return -ENOMEM; } cval->chip = state->chip; @@ -1023,29 +1101,39 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char /* get min/max values */ if (get_ctl_value(cval, GET_MAX, cval->control, &cval->max) < 0 || get_ctl_value(cval, GET_MIN, cval->control, &cval->min) < 0) - snd_printk(KERN_ERR "cannot get min/max values for proc/ext unit\n"); + snd_printd(KERN_ERR "cannot get min/max values for proc/ext control=%d, id=%d\n", cval->control, unitid); + else if (cval->max <= cval->min) + snd_printd(KERN_ERR "invalid min/max values (%d/%d) for proc/ext unit control=%d, id=%d\n", cval->min, cval->max, cval->control, unitid); else cval->initialized = 1; kctl = snd_ctl_new1(&mixer_procunit_ctl, cval); if (! kctl) { - snd_printk(KERN_ERR "cannot malloc kcontrol"); + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); snd_magic_kfree(cval); return -ENOMEM; } kctl->private_free = usb_mixer_elem_free; - if (info->name) - sprintf(kctl->id.name, "%s %s", info->name, valinfo->suffix); + if (check_mapped_name(state, unitid, cval->control, kctl->id.name, sizeof(kctl->id.name))) + ; + else if (info->name) + strcpy(kctl->id.name, info->name); else { nameid = dsc[12 + num_ins + dsc[11 + num_ins]]; - if (nameid) { - int len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); - strcpy(kctl->id.name + len, valinfo->suffix); - } else - sprintf(kctl->id.name, "%s %s", name, valinfo->suffix); + len = 0; + if (nameid) + len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); + if (! len) { + strncpy(kctl->id.name, name, sizeof(kctl->id.name) - 1); + kctl->id.name[sizeof(kctl->id.name)-1] = 0; + } + } + len = strlen(kctl->id.name); + if (len + sizeof(valinfo->suffix) + 1 < sizeof(kctl->id.name)) { + kctl->id.name[len] = ' '; + strcpy(kctl->id.name + len + 1, valinfo->suffix); } - snd_printdd(KERN_INFO "[%d] PU [%s] ch = %d, val = %d/%d\n", cval->id, kctl->id.name, cval->channels, cval->min, cval->max); if ((err = add_control_to_empty(state->chip->card, kctl)) < 0) @@ -1158,7 +1246,7 @@ static void usb_mixer_selector_elem_free(snd_kcontrol_t *kctl) static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned char *desc) { int num_ins = desc[4]; - int i, err, nameid; + int i, err, nameid, len; usb_mixer_elem_info_t *cval; snd_kcontrol_t *kctl; char **namelist; @@ -1175,7 +1263,7 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL); if (! cval) { - snd_printk(KERN_ERR "cannot malloc kcontrol"); + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); return -ENOMEM; } cval->chip = state->chip; @@ -1196,7 +1284,7 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned #define MAX_ITEM_NAME_LEN 64 for (i = 0; i < num_ins; i++) { usb_audio_term_t iterm; - int len = 0; + len = 0; namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL); if (! namelist[i]) { snd_printk(KERN_ERR "cannot malloc\n"); @@ -1214,7 +1302,7 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval); if (! kctl) { - snd_printk(KERN_ERR "cannot malloc kcontrol"); + snd_printk(KERN_ERR "cannot malloc kcontrol\n"); snd_magic_kfree(cval); return -ENOMEM; } @@ -1222,17 +1310,23 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned kctl->private_free = usb_mixer_selector_elem_free; nameid = desc[desc[0] - 1]; - if (nameid) + len = check_mapped_name(state, unitid, 0, kctl->id.name, sizeof(kctl->id.name)); + if (len) + ; + else if (nameid) snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); else { - int len = get_term_name(state, &state->oterm, - kctl->id.name, sizeof(kctl->id.name), 0); + len = get_term_name(state, &state->oterm, + kctl->id.name, sizeof(kctl->id.name), 0); if (! len) len = sprintf(kctl->id.name, "USB"); - if ((state->oterm.type & 0xff00) == 0x0100) - strcpy(kctl->id.name + len, " Capture Source"); - else - strcpy(kctl->id.name + len, " Playback Source"); + if ((state->oterm.type & 0xff00) == 0x0100) { + if (len + 15 < sizeof(kctl->id.name)) + strcpy(kctl->id.name + len, " Capture Source"); + } else { + if (len + 16 < sizeof(kctl->id.name)) + strcpy(kctl->id.name + len, " Playback Source"); + } } snd_printdd(KERN_INFO "[%d] SU [%s] items = %d\n", @@ -1290,6 +1384,8 @@ int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif, unsigned char *buffe unsigned char *desc; mixer_build_t state; int err; + const struct usbmix_ctl_map *map; + struct usb_device_descriptor *dev = &chip->dev->descriptor; strcpy(chip->card->mixername, "USB Mixer"); @@ -1298,6 +1394,15 @@ int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif, unsigned char *buffe state.buffer = buffer; state.buflen = buflen; state.ctrlif = ctrlif; + + /* check the mapping table */ + for (map = usbmix_ctl_maps; map->vendor; map++) { + if (map->vendor == dev->idVendor && map->product == dev->idProduct) { + state.map = map->map; + break; + } + } + desc = NULL; while ((desc = snd_usb_find_csint_desc(buffer, buflen, desc, OUTPUT_TERMINAL, ctrlif, -1)) != NULL) { if (desc[0] < 9) diff --git a/sound/usb/usbmixer_maps.c b/sound/usb/usbmixer_maps.c new file mode 100644 index 000000000000..4d9d53102a6b --- /dev/null +++ b/sound/usb/usbmixer_maps.c @@ -0,0 +1,100 @@ +/* + * Additional mixer mapping + * + * Copyright (c) 2002 by Takashi Iwai + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +struct usbmix_name_map { + int id; + const char *name; + int control; +}; + +struct usbmix_ctl_map { + int vendor; + int product; + const struct usbmix_name_map *map; +}; + +/* + * USB control mappers for SB Exitigy + */ + +/* + * Topology of SB Extigy (see on the wide screen :) + +USB_IN[1] --->FU[2]------------------------------+->MU[16]-->PE[17]-+->FU[18]--+->EU[27]--+->EU[21]-->FU[22]--+->FU[23] > Dig_OUT[24] + ^ | | | | +USB_IN[3] -+->SU[5]-->FU[6]--+->MU[14] ->PE[15]->+ | | | +->FU[25] > Dig_OUT[26] + ^ ^ | | | | +Dig_IN[4] -+ | | | | +->FU[28]---------------------> Spk_OUT[19] + | | | | +Lin-IN[7] -+-->FU[8]---------+ | | +----------------------------------------> Hph_OUT[20] + | | | +Mic-IN[9] --+->FU[10]----------------------------+ | + || | + || +----------------------------------------------------+ + VV V + ++--+->SU[11]-->FU[12] --------------------------------------------------------------------------------------> USB_OUT[13] +*/ + +static struct usbmix_name_map extigy_map[] = { + /* 1: IT pcm */ + { 2, "PCM Playback" }, /* FU */ + /* 3: IT pcm */ + /* 4: IT digital in */ + { 5, "Digital In Playback Source" }, /* SU */ + { 6, "Digital In" }, /* FU */ + /* 7: IT line */ + { 8, "Line Playback" }, /* FU */ + /* 9: IT mic */ + { 10, "Mic Playback" }, /* FU */ + { 11, "Capture Source" }, /* SU */ + { 12, "Capture" }, /* FU */ + /* 13: OT pcm capture */ + /* 14: MU (w/o controls) */ + /* 15: PE (3D enh) */ + /* 16: MU (w/o controls) */ + /* 17: PE (updown) */ /* FIXME: what control? */ + { 18, "Tone Control - Bass", USB_FEATURE_BASS }, /* FU */ + { 18, "Tone Control - Treble", USB_FEATURE_TREBLE }, /* FU */ + { 18, "Master Playback" }, /* FU; others */ + /* 19: OT speaker */ + /* 20: OT headphone */ + { 21, "Digital Out Extension" }, /* EU */ /* FIXME: what? */ + { 22, "Digital Out Playback" }, /* FU */ + { 23, "Digital Out1 Playback" }, /* FU */ /* FIXME: corresponds to 24 */ + /* 24: OT digital out */ + { 25, "Digital Out2 Playback" }, /* FU */ /* FIXME: corresponds to 26 */ + /* 26: OT digital out */ + { 27, "Output Extension" }, /* EU */ /* FIXME: what? */ + /* 28: FU (mute) */ + { 0 } /* terminator */ +}; + + +/* + * Control map entries + */ + +static struct usbmix_ctl_map usbmix_ctl_maps[] = { + { 0x41e, 0x3000, extigy_map }, + { 0 } /* terminator */ +}; + -- cgit v1.2.3 From 70189e27c72678f8c71007d858b6fc3be1e9980b Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 13 Oct 2002 15:23:48 -0700 Subject: [SPARC64]: Kill some port-specific bloat. - Uninline down/up/down_trylock/down_interruptible - Uninline PCI controller cfg space helpers - Uninline ip_fast_csum - Uninline some routines in signal32/sys_sparc32/unaligned - Uninline arch/sparc64/mm/fault.c:get_fault_init - NUM_IVECS need only be IMAP_NR + 1 --- arch/sparc64/kernel/pci.c | 126 ++++++++++++++++++++++++- arch/sparc64/kernel/pci_impl.h | 133 ++------------------------ arch/sparc64/kernel/semaphore.c | 181 ++++++++++++++++++++++++++++++++++- arch/sparc64/kernel/signal32.c | 18 ++-- arch/sparc64/kernel/sparc64_ksyms.c | 8 +- arch/sparc64/kernel/sys_sparc32.c | 16 ++-- arch/sparc64/kernel/unaligned.c | 4 +- arch/sparc64/lib/Makefile | 2 +- arch/sparc64/lib/ipcsum.S | 32 +++++++ arch/sparc64/mm/fault.c | 2 +- include/asm-sparc64/checksum.h | 41 +------- include/asm-sparc64/irq.h | 2 +- include/asm-sparc64/semaphore.h | 182 +----------------------------------- 13 files changed, 373 insertions(+), 374 deletions(-) create mode 100644 arch/sparc64/lib/ipcsum.S (limited to 'include') diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c index 1a2f15996a2e..1aff826a51b0 100644 --- a/arch/sparc64/kernel/pci.c +++ b/arch/sparc64/kernel/pci.c @@ -72,11 +72,135 @@ unsigned char pci_highest_busnum = 0; */ int pci_device_reorder = 0; -spinlock_t pci_poke_lock = SPIN_LOCK_UNLOCKED; volatile int pci_poke_in_progress; volatile int pci_poke_cpu = -1; volatile int pci_poke_faulted; +static spinlock_t pci_poke_lock = SPIN_LOCK_UNLOCKED; + +void pci_config_read8(u8 *addr, u8 *ret) +{ + unsigned long flags; + u8 byte; + + spin_lock_irqsave(&pci_poke_lock, flags); + pci_poke_cpu = smp_processor_id(); + pci_poke_in_progress = 1; + pci_poke_faulted = 0; + __asm__ __volatile__("membar #Sync\n\t" + "lduba [%1] %2, %0\n\t" + "membar #Sync" + : "=r" (byte) + : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L) + : "memory"); + pci_poke_in_progress = 0; + pci_poke_cpu = -1; + if (!pci_poke_faulted) + *ret = byte; + spin_unlock_irqrestore(&pci_poke_lock, flags); +} + +void pci_config_read16(u16 *addr, u16 *ret) +{ + unsigned long flags; + u16 word; + + spin_lock_irqsave(&pci_poke_lock, flags); + pci_poke_cpu = smp_processor_id(); + pci_poke_in_progress = 1; + pci_poke_faulted = 0; + __asm__ __volatile__("membar #Sync\n\t" + "lduha [%1] %2, %0\n\t" + "membar #Sync" + : "=r" (word) + : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L) + : "memory"); + pci_poke_in_progress = 0; + pci_poke_cpu = -1; + if (!pci_poke_faulted) + *ret = word; + spin_unlock_irqrestore(&pci_poke_lock, flags); +} + +void pci_config_read32(u32 *addr, u32 *ret) +{ + unsigned long flags; + u32 dword; + + spin_lock_irqsave(&pci_poke_lock, flags); + pci_poke_cpu = smp_processor_id(); + pci_poke_in_progress = 1; + pci_poke_faulted = 0; + __asm__ __volatile__("membar #Sync\n\t" + "lduwa [%1] %2, %0\n\t" + "membar #Sync" + : "=r" (dword) + : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L) + : "memory"); + pci_poke_in_progress = 0; + pci_poke_cpu = -1; + if (!pci_poke_faulted) + *ret = dword; + spin_unlock_irqrestore(&pci_poke_lock, flags); +} + +void pci_config_write8(u8 *addr, u8 val) +{ + unsigned long flags; + + spin_lock_irqsave(&pci_poke_lock, flags); + pci_poke_cpu = smp_processor_id(); + pci_poke_in_progress = 1; + pci_poke_faulted = 0; + __asm__ __volatile__("membar #Sync\n\t" + "stba %0, [%1] %2\n\t" + "membar #Sync" + : /* no outputs */ + : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L) + : "memory"); + pci_poke_in_progress = 0; + pci_poke_cpu = -1; + spin_unlock_irqrestore(&pci_poke_lock, flags); +} + +void pci_config_write16(u16 *addr, u16 val) +{ + unsigned long flags; + + spin_lock_irqsave(&pci_poke_lock, flags); + pci_poke_cpu = smp_processor_id(); + pci_poke_in_progress = 1; + pci_poke_faulted = 0; + __asm__ __volatile__("membar #Sync\n\t" + "stha %0, [%1] %2\n\t" + "membar #Sync" + : /* no outputs */ + : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L) + : "memory"); + pci_poke_in_progress = 0; + pci_poke_cpu = -1; + spin_unlock_irqrestore(&pci_poke_lock, flags); +} + +void pci_config_write32(u32 *addr, u32 val) +{ + unsigned long flags; + + spin_lock_irqsave(&pci_poke_lock, flags); + pci_poke_cpu = smp_processor_id(); + pci_poke_in_progress = 1; + pci_poke_faulted = 0; + __asm__ __volatile__("membar #Sync\n\t" + "stwa %0, [%1] %2\n\t" + "membar #Sync" + : /* no outputs */ + : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L) + : "memory"); + pci_poke_in_progress = 0; + pci_poke_cpu = -1; + spin_unlock_irqrestore(&pci_poke_lock, flags); +} + /* Probe for all PCI controllers in the system. */ extern void sabre_init(int, char *); extern void psycho_init(int, char *); diff --git a/arch/sparc64/kernel/pci_impl.h b/arch/sparc64/kernel/pci_impl.h index 3d59584891cf..3453ea3e57e6 100644 --- a/arch/sparc64/kernel/pci_impl.h +++ b/arch/sparc64/kernel/pci_impl.h @@ -42,132 +42,11 @@ extern void pci_scan_for_master_abort(struct pci_controller_info *, struct pci_p extern void pci_scan_for_parity_error(struct pci_controller_info *, struct pci_pbm_info *, struct pci_bus *); /* Configuration space access. */ -extern spinlock_t pci_poke_lock; -extern volatile int pci_poke_in_progress; -extern volatile int pci_poke_cpu; -extern volatile int pci_poke_faulted; - -static __inline__ void pci_config_read8(u8 *addr, u8 *ret) -{ - unsigned long flags; - u8 byte; - - spin_lock_irqsave(&pci_poke_lock, flags); - pci_poke_cpu = smp_processor_id(); - pci_poke_in_progress = 1; - pci_poke_faulted = 0; - __asm__ __volatile__("membar #Sync\n\t" - "lduba [%1] %2, %0\n\t" - "membar #Sync" - : "=r" (byte) - : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L) - : "memory"); - pci_poke_in_progress = 0; - pci_poke_cpu = -1; - if (!pci_poke_faulted) - *ret = byte; - spin_unlock_irqrestore(&pci_poke_lock, flags); -} - -static __inline__ void pci_config_read16(u16 *addr, u16 *ret) -{ - unsigned long flags; - u16 word; - - spin_lock_irqsave(&pci_poke_lock, flags); - pci_poke_cpu = smp_processor_id(); - pci_poke_in_progress = 1; - pci_poke_faulted = 0; - __asm__ __volatile__("membar #Sync\n\t" - "lduha [%1] %2, %0\n\t" - "membar #Sync" - : "=r" (word) - : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L) - : "memory"); - pci_poke_in_progress = 0; - pci_poke_cpu = -1; - if (!pci_poke_faulted) - *ret = word; - spin_unlock_irqrestore(&pci_poke_lock, flags); -} - -static __inline__ void pci_config_read32(u32 *addr, u32 *ret) -{ - unsigned long flags; - u32 dword; - - spin_lock_irqsave(&pci_poke_lock, flags); - pci_poke_cpu = smp_processor_id(); - pci_poke_in_progress = 1; - pci_poke_faulted = 0; - __asm__ __volatile__("membar #Sync\n\t" - "lduwa [%1] %2, %0\n\t" - "membar #Sync" - : "=r" (dword) - : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L) - : "memory"); - pci_poke_in_progress = 0; - pci_poke_cpu = -1; - if (!pci_poke_faulted) - *ret = dword; - spin_unlock_irqrestore(&pci_poke_lock, flags); -} - -static __inline__ void pci_config_write8(u8 *addr, u8 val) -{ - unsigned long flags; - - spin_lock_irqsave(&pci_poke_lock, flags); - pci_poke_cpu = smp_processor_id(); - pci_poke_in_progress = 1; - pci_poke_faulted = 0; - __asm__ __volatile__("membar #Sync\n\t" - "stba %0, [%1] %2\n\t" - "membar #Sync" - : /* no outputs */ - : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L) - : "memory"); - pci_poke_in_progress = 0; - pci_poke_cpu = -1; - spin_unlock_irqrestore(&pci_poke_lock, flags); -} - -static __inline__ void pci_config_write16(u16 *addr, u16 val) -{ - unsigned long flags; - - spin_lock_irqsave(&pci_poke_lock, flags); - pci_poke_cpu = smp_processor_id(); - pci_poke_in_progress = 1; - pci_poke_faulted = 0; - __asm__ __volatile__("membar #Sync\n\t" - "stha %0, [%1] %2\n\t" - "membar #Sync" - : /* no outputs */ - : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L) - : "memory"); - pci_poke_in_progress = 0; - pci_poke_cpu = -1; - spin_unlock_irqrestore(&pci_poke_lock, flags); -} - -static __inline__ void pci_config_write32(u32 *addr, u32 val) -{ - unsigned long flags; - - spin_lock_irqsave(&pci_poke_lock, flags); - pci_poke_cpu = smp_processor_id(); - pci_poke_in_progress = 1; - pci_poke_faulted = 0; - __asm__ __volatile__("membar #Sync\n\t" - "stwa %0, [%1] %2\n\t" - "membar #Sync" - : /* no outputs */ - : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E_L) - : "memory"); - pci_poke_in_progress = 0; - pci_poke_cpu = -1; - spin_unlock_irqrestore(&pci_poke_lock, flags); -} +extern void pci_config_read8(u8 *addr, u8 *ret); +extern void pci_config_read16(u16 *addr, u16 *ret); +extern void pci_config_read32(u32 *addr, u32 *ret); +extern void pci_config_write8(u8 *addr, u8 val); +extern void pci_config_write16(u16 *addr, u16 val); +extern void pci_config_write32(u32 *addr, u32 val); #endif /* !(PCI_IMPL_H) */ diff --git a/arch/sparc64/kernel/semaphore.c b/arch/sparc64/kernel/semaphore.c index bfb72f4041fd..4ce2f9369019 100644 --- a/arch/sparc64/kernel/semaphore.c +++ b/arch/sparc64/kernel/semaphore.c @@ -40,13 +40,57 @@ static __inline__ int __sem_update_count(struct semaphore *sem, int incr) return old_count; } -void __up(struct semaphore *sem) +static void __up(struct semaphore *sem) { __sem_update_count(sem, 1); wake_up(&sem->wait); } -void __down(struct semaphore * sem) +void up(struct semaphore *sem) +{ + /* This atomically does: + * old_val = sem->count; + * new_val = sem->count + 1; + * sem->count = new_val; + * if (old_val < 0) + * __up(sem); + * + * The (old_val < 0) test is equivalent to + * the more straightforward (new_val <= 0), + * but it is easier to test the former because + * of how the CAS instruction works. + */ + + __asm__ __volatile__("\n" +" ! up sem(%0)\n" +" membar #StoreLoad | #LoadLoad\n" +"1: lduw [%0], %%g5\n" +" add %%g5, 1, %%g7\n" +" cas [%0], %%g5, %%g7\n" +" cmp %%g5, %%g7\n" +" bne,pn %%icc, 1b\n" +" addcc %%g7, 1, %%g0\n" +" ble,pn %%icc, 3f\n" +" membar #StoreLoad | #StoreStore\n" +"2:\n" +" .subsection 2\n" +"3: mov %0, %%g5\n" +" save %%sp, -160, %%sp\n" +" mov %%g1, %%l1\n" +" mov %%g2, %%l2\n" +" mov %%g3, %%l3\n" +" call %1\n" +" mov %%g5, %%o0\n" +" mov %%l1, %%g1\n" +" mov %%l2, %%g2\n" +" ba,pt %%xcc, 2b\n" +" restore %%l3, %%g0, %%g3\n" +" .previous\n" + : : "r" (sem), "i" (__up) + : "g5", "g7", "memory", "cc"); +} + +static void __down(struct semaphore * sem) { struct task_struct *tsk = current; DECLARE_WAITQUEUE(wait, tsk); @@ -64,7 +108,90 @@ void __down(struct semaphore * sem) wake_up(&sem->wait); } -int __down_interruptible(struct semaphore * sem) +void down(struct semaphore *sem) +{ + /* This atomically does: + * old_val = sem->count; + * new_val = sem->count - 1; + * sem->count = new_val; + * if (old_val < 1) + * __down(sem); + * + * The (old_val < 1) test is equivalent to + * the more straightforward (new_val < 0), + * but it is easier to test the former because + * of how the CAS instruction works. + */ + + __asm__ __volatile__("\n" +" ! down sem(%0)\n" +"1: lduw [%0], %%g5\n" +" sub %%g5, 1, %%g7\n" +" cas [%0], %%g5, %%g7\n" +" cmp %%g5, %%g7\n" +" bne,pn %%icc, 1b\n" +" cmp %%g7, 1\n" +" bl,pn %%icc, 3f\n" +" membar #StoreLoad | #StoreStore\n" +"2:\n" +" .subsection 2\n" +"3: mov %0, %%g5\n" +" save %%sp, -160, %%sp\n" +" mov %%g1, %%l1\n" +" mov %%g2, %%l2\n" +" mov %%g3, %%l3\n" +" call %1\n" +" mov %%g5, %%o0\n" +" mov %%l1, %%g1\n" +" mov %%l2, %%g2\n" +" ba,pt %%xcc, 2b\n" +" restore %%l3, %%g0, %%g3\n" +" .previous\n" + : : "r" (sem), "i" (__down) + : "g5", "g7", "memory", "cc"); +} + +int down_trylock(struct semaphore *sem) +{ + int ret; + + /* This atomically does: + * old_val = sem->count; + * new_val = sem->count - 1; + * if (old_val < 1) { + * ret = 1; + * } else { + * sem->count = new_val; + * ret = 0; + * } + * + * The (old_val < 1) test is equivalent to + * the more straightforward (new_val < 0), + * but it is easier to test the former because + * of how the CAS instruction works. + */ + + __asm__ __volatile__("\n" +" ! down_trylock sem(%1) ret(%0)\n" +"1: lduw [%1], %%g5\n" +" sub %%g5, 1, %%g7\n" +" cmp %%g5, 1\n" +" bl,pn %%icc, 2f\n" +" mov 1, %0\n" +" cas [%1], %%g5, %%g7\n" +" cmp %%g5, %%g7\n" +" bne,pn %%icc, 1b\n" +" mov 0, %0\n" +" membar #StoreLoad | #StoreStore\n" +"2:\n" + : "=&r" (ret) + : "r" (sem) + : "g5", "g7", "memory", "cc"); + + return ret; +} + +static int __down_interruptible(struct semaphore * sem) { int retval = 0; struct task_struct *tsk = current; @@ -87,3 +214,51 @@ int __down_interruptible(struct semaphore * sem) wake_up(&sem->wait); return retval; } + +int down_interruptible(struct semaphore *sem) +{ + int ret = 0; + + /* This atomically does: + * old_val = sem->count; + * new_val = sem->count - 1; + * sem->count = new_val; + * if (old_val < 1) + * ret = __down_interruptible(sem); + * + * The (old_val < 1) test is equivalent to + * the more straightforward (new_val < 0), + * but it is easier to test the former because + * of how the CAS instruction works. + */ + + __asm__ __volatile__("\n" +" ! down_interruptible sem(%2) ret(%0)\n" +"1: lduw [%2], %%g5\n" +" sub %%g5, 1, %%g7\n" +" cas [%2], %%g5, %%g7\n" +" cmp %%g5, %%g7\n" +" bne,pn %%icc, 1b\n" +" cmp %%g7, 1\n" +" bl,pn %%icc, 3f\n" +" membar #StoreLoad | #StoreStore\n" +"2:\n" +" .subsection 2\n" +"3: mov %2, %%g5\n" +" save %%sp, -160, %%sp\n" +" mov %%g1, %%l1\n" +" mov %%g2, %%l2\n" +" mov %%g3, %%l3\n" +" call %3\n" +" mov %%g5, %%o0\n" +" mov %%l1, %%g1\n" +" mov %%l2, %%g2\n" +" mov %%l3, %%g3\n" +" ba,pt %%xcc, 2b\n" +" restore %%o0, %%g0, %0\n" +" .previous\n" + : "=r" (ret) + : "0" (ret), "r" (sem), "i" (__down_interruptible) + : "g5", "g7", "memory", "cc"); + return ret; +} diff --git a/arch/sparc64/kernel/signal32.c b/arch/sparc64/kernel/signal32.c index 1dad12270c22..84a28f319c7b 100644 --- a/arch/sparc64/kernel/signal32.c +++ b/arch/sparc64/kernel/signal32.c @@ -230,7 +230,7 @@ asmlinkage void do_rt_sigsuspend32(u32 uset, size_t sigsetsize, struct pt_regs * } } -static inline int restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t *fpu) +static int restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t *fpu) { unsigned long *fpregs = current_thread_info()->fpregs; unsigned long fprs; @@ -477,7 +477,7 @@ static int invalid_frame_pointer(void *fp, int fplen) return 0; } -static inline void *get_sigframe(struct sigaction *sa, struct pt_regs *regs, unsigned long framesize) +static void *get_sigframe(struct sigaction *sa, struct pt_regs *regs, unsigned long framesize) { unsigned long sp; @@ -645,7 +645,7 @@ sigsegv: } -static inline int save_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t *fpu) +static int save_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t *fpu) { unsigned long *fpregs = current_thread_info()->fpregs; unsigned long fprs; @@ -665,8 +665,8 @@ static inline int save_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t *fpu) return err; } -static inline void new_setup_frame32(struct k_sigaction *ka, struct pt_regs *regs, - int signo, sigset_t *oldset) +static void new_setup_frame32(struct k_sigaction *ka, struct pt_regs *regs, + int signo, sigset_t *oldset) { struct new_signal_frame32 *sf; int sigframe_size; @@ -790,7 +790,7 @@ sigsegv: } /* Setup a Solaris stack frame */ -static inline void +static void setup_svr4_frame32(struct sigaction *sa, unsigned long pc, unsigned long npc, struct pt_regs *regs, int signr, sigset_t *oldset) { @@ -1089,9 +1089,9 @@ sigsegv: do_exit(SIGSEGV); } -static inline void setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs, - unsigned long signr, sigset_t *oldset, - siginfo_t *info) +static void setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs, + unsigned long signr, sigset_t *oldset, + siginfo_t *info) { struct rt_signal_frame32 *sf; int sigframe_size; diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index 31110addbd92..a68ab5b7b15e 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -161,9 +161,10 @@ EXPORT_SYMBOL(smp_call_function); #endif /* semaphores */ -EXPORT_SYMBOL(__down); -EXPORT_SYMBOL(__down_interruptible); -EXPORT_SYMBOL(__up); +EXPORT_SYMBOL(down); +EXPORT_SYMBOL(down_trylock); +EXPORT_SYMBOL(down_interruptible); +EXPORT_SYMBOL(up); /* Atomic counter implementation. */ EXPORT_SYMBOL(__atomic_add); @@ -332,6 +333,7 @@ EXPORT_SYMBOL(__strncmp); EXPORT_SYMBOL(__memmove); EXPORT_SYMBOL(csum_partial_copy_sparc64); +EXPORT_SYMBOL(ip_fast_csum); /* Moving data to/from userspace. */ EXPORT_SYMBOL(__copy_to_user); diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 9eff2aad6314..ddf3b33f25c2 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -273,7 +273,7 @@ struct itimerval32 struct timeval32 it_value; }; -static inline long get_tv32(struct timeval *o, struct timeval32 *i) +static long get_tv32(struct timeval *o, struct timeval32 *i) { return (!access_ok(VERIFY_READ, tv32, sizeof(*tv32)) || (__get_user(o->tv_sec, &i->tv_sec) | @@ -296,7 +296,7 @@ static inline long get_it32(struct itimerval *o, struct itimerval32 *i) __get_user(o->it_value.tv_usec, &i->it_value.tv_usec))); } -static inline long put_it32(struct itimerval32 *o, struct itimerval *i) +static long put_it32(struct itimerval32 *o, struct itimerval *i) { return (!access_ok(VERIFY_WRITE, i32, sizeof(*i32)) || (__put_user(i->it_interval.tv_sec, &o->it_interval.tv_sec) | @@ -890,7 +890,7 @@ asmlinkage long sys32_fcntl64(unsigned int fd, unsigned int cmd, unsigned long a return sys32_fcntl(fd, cmd, arg); } -static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) +static int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) { int err; @@ -1272,8 +1272,7 @@ out: * 64-bit unsigned longs. */ -static inline int -get_fd_set32(unsigned long n, unsigned long *fdset, u32 *ufdset) +static int get_fd_set32(unsigned long n, unsigned long *fdset, u32 *ufdset) { if (ufdset) { unsigned long odd; @@ -1303,8 +1302,7 @@ get_fd_set32(unsigned long n, unsigned long *fdset, u32 *ufdset) return 0; } -static inline void -set_fd_set32(unsigned long n, u32 *ufdset, unsigned long *fdset) +static void set_fd_set32(unsigned long n, u32 *ufdset, unsigned long *fdset) { unsigned long odd; @@ -2217,8 +2215,8 @@ static inline int iov_from_user32_to_kern(struct iovec *kiov, return tot_len; } -static inline int msghdr_from_user32_to_kern(struct msghdr *kmsg, - struct msghdr32 *umsg) +static int msghdr_from_user32_to_kern(struct msghdr *kmsg, + struct msghdr32 *umsg) { u32 tmp1, tmp2, tmp3; int err; diff --git a/arch/sparc64/kernel/unaligned.c b/arch/sparc64/kernel/unaligned.c index d2be1f3629de..d5f8f8eb0360 100644 --- a/arch/sparc64/kernel/unaligned.c +++ b/arch/sparc64/kernel/unaligned.c @@ -149,8 +149,8 @@ static unsigned long *fetch_reg_addr(unsigned int reg, struct pt_regs *regs) } } -static inline unsigned long compute_effective_address(struct pt_regs *regs, - unsigned int insn, unsigned int rd) +static unsigned long compute_effective_address(struct pt_regs *regs, + unsigned int insn, unsigned int rd) { unsigned int rs1 = (insn >> 14) & 0x1f; unsigned int rs2 = insn & 0x1f; diff --git a/arch/sparc64/lib/Makefile b/arch/sparc64/lib/Makefile index 6d3e2a7aeaca..421f1e403bcf 100644 --- a/arch/sparc64/lib/Makefile +++ b/arch/sparc64/lib/Makefile @@ -10,6 +10,6 @@ obj-y := PeeCeeI.o blockops.o debuglocks.o strlen.o strncmp.o \ VIScopy.o VISbzero.o VISmemset.o VIScsum.o VIScsumcopy.o \ VIScsumcopyusr.o VISsave.o atomic.o rwlock.o bitops.o \ dec_and_lock.o U3memcpy.o U3copy_from_user.o U3copy_to_user.o \ - U3copy_in_user.o mcount.o + U3copy_in_user.o mcount.o ipcsum.o include $(TOPDIR)/Rules.make diff --git a/arch/sparc64/lib/ipcsum.S b/arch/sparc64/lib/ipcsum.S new file mode 100644 index 000000000000..e7d349facf35 --- /dev/null +++ b/arch/sparc64/lib/ipcsum.S @@ -0,0 +1,32 @@ + .text + .align 32 + .globl ip_fast_csum +ip_fast_csum: /* %o0 = iph, %o1 = ihl */ + sub %o1, 4, %g7 + lduw [%o0 + 0x00], %o2 + lduw [%o0 + 0x04], %g2 + lduw [%o0 + 0x08], %g3 + addcc %g2, %o2, %o2 + lduw [%o0 + 0x0c], %g2 + addccc %g3, %o2, %o2 + lduw [%o0 + 0x10], %g3 + + addccc %g2, %o2, %o2 + addc %o2, %g0, %o2 +1: addcc %g3, %o2, %o2 + add %o0, 4, %o0 + addccc %o2, %g0, %o2 + subcc %g7, 1, %g7 + be,a,pt %icc, 2f + sll %o2, 16, %g2 + + lduw [%o0 + 0x10], %g3 + ba,pt %xcc, 1b + nop +2: addcc %o2, %g2, %g2 + srl %g2, 16, %o2 + addc %o2, %g0, %o2 + xnor %g0, %o2, %o2 + set 0xffff, %o1 + retl + and %o2, %o1, %o0 diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c index f34184be0c71..e5aa61b9f52d 100644 --- a/arch/sparc64/mm/fault.c +++ b/arch/sparc64/mm/fault.c @@ -222,7 +222,7 @@ static void do_fault_siginfo(int code, int sig, unsigned long address) extern int handle_ldf_stq(u32, struct pt_regs *); extern int handle_ld_nf(u32, struct pt_regs *); -static inline unsigned int get_fault_insn(struct pt_regs *regs, unsigned int insn) +static unsigned int get_fault_insn(struct pt_regs *regs, unsigned int insn) { if (!insn) { if (!regs->tpc || (regs->tpc & 0x3)) diff --git a/include/asm-sparc64/checksum.h b/include/asm-sparc64/checksum.h index 6cfa55be0f09..a7d31c896881 100644 --- a/include/asm-sparc64/checksum.h +++ b/include/asm-sparc64/checksum.h @@ -78,45 +78,8 @@ csum_and_copy_to_user(const char *src, char *dst, int len, /* ihl is always 5 or greater, almost always is 5, and iph is word aligned * the majority of the time. */ -static __inline__ unsigned short ip_fast_csum(__const__ unsigned char *iph, - unsigned int ihl) -{ - unsigned short sum; - - /* Note: We must read %2 before we touch %0 for the first time, - * because GCC can legitimately use the same register for - * both operands. - */ - __asm__ __volatile__( -" sub %2, 4, %%g7 ! IEU0\n" -" lduw [%1 + 0x00], %0 ! Load Group\n" -" lduw [%1 + 0x04], %%g2 ! Load Group\n" -" lduw [%1 + 0x08], %%g3 ! Load Group\n" -" addcc %%g2, %0, %0 ! IEU1 1 Load Bubble + Group\n" -" lduw [%1 + 0x0c], %%g2 ! Load\n" -" addccc %%g3, %0, %0 ! Sngle Group no Bubble\n" -" lduw [%1 + 0x10], %%g3 ! Load Group\n" -" addccc %%g2, %0, %0 ! Sngle Group no Bubble\n" -" addc %0, %%g0, %0 ! Sngle Group\n" -"1: addcc %%g3, %0, %0 ! IEU1 Group no Bubble\n" -" add %1, 4, %1 ! IEU0\n" -" addccc %0, %%g0, %0 ! Sngle Group no Bubble\n" -" subcc %%g7, 1, %%g7 ! IEU1 Group\n" -" be,a,pt %%icc, 2f ! CTI\n" -" sll %0, 16, %%g2 ! IEU0\n" -" lduw [%1 + 0x10], %%g3 ! Load Group\n" -" ba,pt %%xcc, 1b ! CTI\n" -" nop ! IEU0\n" -"2: addcc %0, %%g2, %%g2 ! IEU1 Group\n" -" srl %%g2, 16, %0 ! IEU0 Group regdep XXX Scheisse!\n" -" addc %0, %%g0, %0 ! Sngle Group\n" -" xnor %%g0, %0, %0 ! IEU0 Group\n" -" srl %0, 0, %0 ! IEU0 Group XXX Scheisse!\n" - : "=r" (sum), "=&r" (iph) - : "r" (ihl), "1" (iph) - : "g2", "g3", "g7", "cc"); - return sum; -} +extern unsigned short ip_fast_csum(__const__ unsigned char *iph, + unsigned int ihl); /* Fold a partial checksum without adding pseudo headers. */ static __inline__ unsigned short csum_fold(unsigned int sum) diff --git a/include/asm-sparc64/irq.h b/include/asm-sparc64/irq.h index a56c528e9af4..397744bb33d7 100644 --- a/include/asm-sparc64/irq.h +++ b/include/asm-sparc64/irq.h @@ -93,7 +93,7 @@ extern unsigned char dma_sync_reg_table_entry; #define IBF_MULTI 0x08 /* On PCI, indicates shared bucket. */ #define IBF_INPROGRESS 0x10 /* IRQ is being serviced. */ -#define NUM_IVECS 8192 +#define NUM_IVECS (IMAP_INR + 1) extern struct ino_bucket ivector_table[NUM_IVECS]; #define __irq_ino(irq) \ diff --git a/include/asm-sparc64/semaphore.h b/include/asm-sparc64/semaphore.h index 2aaa3160115e..7419dd88b49e 100644 --- a/include/asm-sparc64/semaphore.h +++ b/include/asm-sparc64/semaphore.h @@ -47,184 +47,10 @@ static inline void init_MUTEX_LOCKED (struct semaphore *sem) sema_init(sem, 0); } -extern void __down(struct semaphore * sem); -extern int __down_interruptible(struct semaphore * sem); -extern void __up(struct semaphore * sem); - -static __inline__ void down(struct semaphore * sem) -{ - /* This atomically does: - * old_val = sem->count; - * new_val = sem->count - 1; - * sem->count = new_val; - * if (old_val < 1) - * __down(sem); - * - * The (old_val < 1) test is equivalent to - * the more straightforward (new_val < 0), - * but it is easier to test the former because - * of how the CAS instruction works. - */ - - __asm__ __volatile__("\n" -" ! down sem(%0)\n" -"1: lduw [%0], %%g5\n" -" sub %%g5, 1, %%g7\n" -" cas [%0], %%g5, %%g7\n" -" cmp %%g5, %%g7\n" -" bne,pn %%icc, 1b\n" -" cmp %%g7, 1\n" -" bl,pn %%icc, 3f\n" -" membar #StoreLoad | #StoreStore\n" -"2:\n" -" .subsection 2\n" -"3: mov %0, %%g5\n" -" save %%sp, -160, %%sp\n" -" mov %%g1, %%l1\n" -" mov %%g2, %%l2\n" -" mov %%g3, %%l3\n" -" call %1\n" -" mov %%g5, %%o0\n" -" mov %%l1, %%g1\n" -" mov %%l2, %%g2\n" -" ba,pt %%xcc, 2b\n" -" restore %%l3, %%g0, %%g3\n" -" .previous\n" - : : "r" (sem), "i" (__down) - : "g5", "g7", "memory", "cc"); -} - -static __inline__ int down_interruptible(struct semaphore *sem) -{ - int ret = 0; - - /* This atomically does: - * old_val = sem->count; - * new_val = sem->count - 1; - * sem->count = new_val; - * if (old_val < 1) - * ret = __down_interruptible(sem); - * - * The (old_val < 1) test is equivalent to - * the more straightforward (new_val < 0), - * but it is easier to test the former because - * of how the CAS instruction works. - */ - - __asm__ __volatile__("\n" -" ! down_interruptible sem(%2) ret(%0)\n" -"1: lduw [%2], %%g5\n" -" sub %%g5, 1, %%g7\n" -" cas [%2], %%g5, %%g7\n" -" cmp %%g5, %%g7\n" -" bne,pn %%icc, 1b\n" -" cmp %%g7, 1\n" -" bl,pn %%icc, 3f\n" -" membar #StoreLoad | #StoreStore\n" -"2:\n" -" .subsection 2\n" -"3: mov %2, %%g5\n" -" save %%sp, -160, %%sp\n" -" mov %%g1, %%l1\n" -" mov %%g2, %%l2\n" -" mov %%g3, %%l3\n" -" call %3\n" -" mov %%g5, %%o0\n" -" mov %%l1, %%g1\n" -" mov %%l2, %%g2\n" -" mov %%l3, %%g3\n" -" ba,pt %%xcc, 2b\n" -" restore %%o0, %%g0, %0\n" -" .previous\n" - : "=r" (ret) - : "0" (ret), "r" (sem), "i" (__down_interruptible) - : "g5", "g7", "memory", "cc"); - return ret; -} - -static __inline__ int down_trylock(struct semaphore *sem) -{ - int ret; - - /* This atomically does: - * old_val = sem->count; - * new_val = sem->count - 1; - * if (old_val < 1) { - * ret = 1; - * } else { - * sem->count = new_val; - * ret = 0; - * } - * - * The (old_val < 1) test is equivalent to - * the more straightforward (new_val < 0), - * but it is easier to test the former because - * of how the CAS instruction works. - */ - - __asm__ __volatile__("\n" -" ! down_trylock sem(%1) ret(%0)\n" -"1: lduw [%1], %%g5\n" -" sub %%g5, 1, %%g7\n" -" cmp %%g5, 1\n" -" bl,pn %%icc, 2f\n" -" mov 1, %0\n" -" cas [%1], %%g5, %%g7\n" -" cmp %%g5, %%g7\n" -" bne,pn %%icc, 1b\n" -" mov 0, %0\n" -" membar #StoreLoad | #StoreStore\n" -"2:\n" - : "=&r" (ret) - : "r" (sem) - : "g5", "g7", "memory", "cc"); - - return ret; -} - -static __inline__ void up(struct semaphore * sem) -{ - /* This atomically does: - * old_val = sem->count; - * new_val = sem->count + 1; - * sem->count = new_val; - * if (old_val < 0) - * __up(sem); - * - * The (old_val < 0) test is equivalent to - * the more straightforward (new_val <= 0), - * but it is easier to test the former because - * of how the CAS instruction works. - */ - - __asm__ __volatile__("\n" -" ! up sem(%0)\n" -" membar #StoreLoad | #LoadLoad\n" -"1: lduw [%0], %%g5\n" -" add %%g5, 1, %%g7\n" -" cas [%0], %%g5, %%g7\n" -" cmp %%g5, %%g7\n" -" bne,pn %%icc, 1b\n" -" addcc %%g7, 1, %%g0\n" -" ble,pn %%icc, 3f\n" -" membar #StoreLoad | #StoreStore\n" -"2:\n" -" .subsection 2\n" -"3: mov %0, %%g5\n" -" save %%sp, -160, %%sp\n" -" mov %%g1, %%l1\n" -" mov %%g2, %%l2\n" -" mov %%g3, %%l3\n" -" call %1\n" -" mov %%g5, %%o0\n" -" mov %%l1, %%g1\n" -" mov %%l2, %%g2\n" -" ba,pt %%xcc, 2b\n" -" restore %%l3, %%g0, %%g3\n" -" .previous\n" - : : "r" (sem), "i" (__up) - : "g5", "g7", "memory", "cc"); -} +extern void up(struct semaphore *sem); +extern void down(struct semaphore *sem); +extern int down_trylock(struct semaphore *sem); +extern int down_interruptible(struct semaphore *sem); #endif /* __KERNEL__ */ -- cgit v1.2.3 From ba4d161345176c1cc551ca3fbaaab8ebc790d49c Mon Sep 17 00:00:00 2001 From: Maksim Krasnyanskiy Date: Sun, 13 Oct 2002 16:23:01 -0700 Subject: Consistent naming for Bluetooth function and constants. Some of them were named like BT_XXX and bt_xxx others BLUEZ_XXX and bluez_xxx. From now on use BT_XXX and bt_xxx throughout Bluetooth code, including CONFIG_ defines. Clean up small typos and misspelling along the way. --- drivers/Makefile | 2 +- drivers/bluetooth/Config.help | 18 +++---- drivers/bluetooth/Config.in | 22 ++++---- drivers/bluetooth/Makefile | 16 +++--- drivers/bluetooth/bluecard_cs.c | 6 +-- drivers/bluetooth/bt3c_cs.c | 4 +- drivers/bluetooth/dtl1_cs.c | 6 +-- drivers/bluetooth/hci_bcsp.c | 14 ++--- drivers/bluetooth/hci_h4.c | 6 +-- drivers/bluetooth/hci_ldisc.c | 20 +++---- drivers/bluetooth/hci_usb.c | 16 +++--- drivers/bluetooth/hci_vhci.c | 8 +-- include/net/bluetooth/bluetooth.h | 76 ++++++++++---------------- include/net/bluetooth/hci.h | 2 +- net/Makefile | 2 +- net/bluetooth/Config.help | 6 +-- net/bluetooth/Makefile | 10 ++-- net/bluetooth/af_bluetooth.c | 111 +++++++++++++++++++------------------- net/bluetooth/bnep/Config.help | 6 +-- net/bluetooth/bnep/Config.in | 8 +-- net/bluetooth/bnep/Makefile | 2 +- net/bluetooth/bnep/core.c | 14 ++--- net/bluetooth/bnep/netdev.c | 12 ++--- net/bluetooth/bnep/sock.c | 8 +-- net/bluetooth/hci_conn.c | 2 +- net/bluetooth/hci_core.c | 24 +++------ net/bluetooth/hci_event.c | 20 +++---- net/bluetooth/hci_sock.c | 20 +++---- net/bluetooth/l2cap.c | 80 +++++++++++++-------------- net/bluetooth/lib.c | 8 +-- net/bluetooth/rfcomm/Config.help | 4 +- net/bluetooth/rfcomm/Config.in | 6 +-- net/bluetooth/rfcomm/Makefile | 4 +- net/bluetooth/rfcomm/core.c | 24 ++++----- net/bluetooth/rfcomm/sock.c | 54 +++++++++---------- net/bluetooth/rfcomm/tty.c | 2 +- net/bluetooth/sco.c | 64 +++++++++++----------- net/bluetooth/syms.c | 30 +++++------ 38 files changed, 353 insertions(+), 384 deletions(-) (limited to 'include') diff --git a/drivers/Makefile b/drivers/Makefile index 958ecc4df984..2224ec9303f6 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -38,7 +38,7 @@ obj-$(CONFIG_I2O) += message/ obj-$(CONFIG_I2C) += i2c/ obj-$(CONFIG_PHONE) += telephony/ obj-$(CONFIG_MD) += md/ -obj-$(CONFIG_BLUEZ) += bluetooth/ +obj-$(CONFIG_BT) += bluetooth/ obj-$(CONFIG_HOTPLUG_PCI) += hotplug/ obj-$(CONFIG_ISDN_BOOL) += isdn/ diff --git a/drivers/bluetooth/Config.help b/drivers/bluetooth/Config.help index 6196615c7ec2..212fec68fdb3 100644 --- a/drivers/bluetooth/Config.help +++ b/drivers/bluetooth/Config.help @@ -1,5 +1,5 @@ HCI UART driver -CONFIG_BLUEZ_HCIUART +CONFIG_BT_HCIUART Bluetooth HCI UART driver. This driver is required if you want to use Bluetooth devices with serial port interface. You will also need this driver if you have @@ -10,7 +10,7 @@ CONFIG_BLUEZ_HCIUART kernel or say M to compile it as module (hci_uart.o). HCI UART (H4) protocol support -CONFIG_BLUEZ_HCIUART_H4 +CONFIG_BT_HCIUART_H4 UART (H4) is serial protocol for communication between Bluetooth device and host. This protocol is required for most Bluetooth devices with UART interface, including PCMCIA and CF cards. @@ -18,7 +18,7 @@ CONFIG_BLUEZ_HCIUART_H4 Say Y here to compile support for HCI UART (H4) protocol. HCI BCSP protocol support -CONFIG_BLUEZ_HCIUART_BCSP +CONFIG_BT_HCIUART_BCSP BCSP (BlueCore Serial Protocol) is serial protocol for communication between Bluetooth device and host. This protocol is required for non USB Bluetooth devices based on CSR BlueCore chip, including PCMCIA and @@ -27,7 +27,7 @@ CONFIG_BLUEZ_HCIUART_BCSP Say Y here to compile support for HCI BCSP protocol. HCI USB driver -CONFIG_BLUEZ_HCIUSB +CONFIG_BT_HCIUSB Bluetooth HCI USB driver. This driver is required if you want to use Bluetooth devices with USB interface. @@ -36,7 +36,7 @@ CONFIG_BLUEZ_HCIUSB kernel or say M to compile it as module (hci_usb.o). HCI USB zero packet support -CONFIG_BLUEZ_USB_ZERO_PACKET +CONFIG_BT_USB_ZERO_PACKET Support for USB zero packets. This option is provided only as a work around for buggy Bluetooth USB devices. Do _not_ enable it unless you know for sure that your device @@ -44,7 +44,7 @@ CONFIG_BLUEZ_USB_ZERO_PACKET Most people should say N here. HCI VHCI Virtual HCI device driver -CONFIG_BLUEZ_HCIVHCI +CONFIG_BT_HCIVHCI Bluetooth Virtual HCI device driver. This driver is required if you want to use HCI Emulation software. @@ -52,7 +52,7 @@ CONFIG_BLUEZ_HCIVHCI kernel or say M to compile it as module (hci_vhci.o). HCI DTL1 (PC Card) device driver -CONFIG_BLUEZ_HCIDTL1 +CONFIG_BT_HCIDTL1 Bluetooth HCI DTL1 (PC Card) driver. This driver provides support for Bluetooth PCMCIA devices with Nokia DTL1 interface: @@ -63,7 +63,7 @@ CONFIG_BLUEZ_HCIDTL1 kernel or say M to compile it as module (dtl1_cs.o). HCI BT3C (PC Card) device driver -CONFIG_BLUEZ_HCIBT3C +CONFIG_BT_HCIBT3C Bluetooth HCI BT3C (PC Card) driver. This driver provides support for Bluetooth PCMCIA devices with 3Com BT3C interface: @@ -77,7 +77,7 @@ CONFIG_BLUEZ_HCIBT3C kernel or say M to compile it as module (bt3c_cs.o). HCI BlueCard (PC Card) device driver -CONFIG_BLUEZ_HCIBLUECARD +CONFIG_BT_HCIBLUECARD Bluetooth HCI BlueCard (PC Card) driver. This driver provides support for Bluetooth PCMCIA devices with Anycom BlueCard interface: diff --git a/drivers/bluetooth/Config.in b/drivers/bluetooth/Config.in index a411065d23b7..46f37275ff76 100644 --- a/drivers/bluetooth/Config.in +++ b/drivers/bluetooth/Config.in @@ -1,23 +1,23 @@ mainmenu_option next_comment comment 'Bluetooth device drivers' -dep_tristate 'HCI USB driver' CONFIG_BLUEZ_HCIUSB $CONFIG_BLUEZ $CONFIG_USB -if [ "$CONFIG_BLUEZ_HCIUSB" != "n" ]; then - bool ' USB zero packet support' CONFIG_BLUEZ_USB_ZERO_PACKET +dep_tristate 'HCI USB driver' CONFIG_BT_HCIUSB $CONFIG_BT $CONFIG_USB +if [ "$CONFIG_BT_HCIUSB" != "n" ]; then + bool ' USB zero packet support' CONFIG_BT_USB_ZERO_PACKET fi -dep_tristate 'HCI UART driver' CONFIG_BLUEZ_HCIUART $CONFIG_BLUEZ -if [ "$CONFIG_BLUEZ_HCIUART" != "n" ]; then - bool ' UART (H4) protocol support' CONFIG_BLUEZ_HCIUART_H4 - bool ' BCSP protocol support' CONFIG_BLUEZ_HCIUART_BCSP +dep_tristate 'HCI UART driver' CONFIG_BT_HCIUART $CONFIG_BT +if [ "$CONFIG_BT_HCIUART" != "n" ]; then + bool ' UART (H4) protocol support' CONFIG_BT_HCIUART_H4 + bool ' BCSP protocol support' CONFIG_BT_HCIUART_BCSP fi -dep_tristate 'HCI DTL1 (PC Card) driver' CONFIG_BLUEZ_HCIDTL1 $CONFIG_PCMCIA $CONFIG_BLUEZ +dep_tristate 'HCI DTL1 (PC Card) driver' CONFIG_BT_HCIDTL1 $CONFIG_PCMCIA $CONFIG_BT -dep_tristate 'HCI BT3C (PC Card) driver' CONFIG_BLUEZ_HCIBT3C $CONFIG_PCMCIA $CONFIG_BLUEZ +dep_tristate 'HCI BT3C (PC Card) driver' CONFIG_BT_HCIBT3C $CONFIG_PCMCIA $CONFIG_BT -dep_tristate 'HCI BlueCard (PC Card) driver' CONFIG_BLUEZ_HCIBLUECARD $CONFIG_PCMCIA $CONFIG_BLUEZ +dep_tristate 'HCI BlueCard (PC Card) driver' CONFIG_BT_HCIBLUECARD $CONFIG_PCMCIA $CONFIG_BT -dep_tristate 'HCI VHCI (Virtual HCI device) driver' CONFIG_BLUEZ_HCIVHCI $CONFIG_BLUEZ +dep_tristate 'HCI VHCI (Virtual HCI device) driver' CONFIG_BT_HCIVHCI $CONFIG_BT endmenu diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 15e76c032f2c..1fbd6f052b07 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -2,16 +2,16 @@ # Makefile for the Linux Bluetooth HCI device drivers. # -obj-$(CONFIG_BLUEZ_HCIUSB) += hci_usb.o -obj-$(CONFIG_BLUEZ_HCIVHCI) += hci_vhci.o -obj-$(CONFIG_BLUEZ_HCIUART) += hci_uart.o -obj-$(CONFIG_BLUEZ_HCIDTL1) += dtl1_cs.o -obj-$(CONFIG_BLUEZ_HCIBT3C) += bt3c_cs.o -obj-$(CONFIG_BLUEZ_HCIBLUECARD) += bluecard_cs.o +obj-$(CONFIG_BT_HCIUSB) += hci_usb.o +obj-$(CONFIG_BT_HCIVHCI) += hci_vhci.o +obj-$(CONFIG_BT_HCIUART) += hci_uart.o +obj-$(CONFIG_BT_HCIDTL1) += dtl1_cs.o +obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o +obj-$(CONFIG_BT_HCIBLUECARD) += bluecard_cs.o hci_uart-y := hci_ldisc.o -hci_uart-$(CONFIG_BLUEZ_HCIUART_H4) += hci_h4.o -hci_uart-$(CONFIG_BLUEZ_HCIUART_BCSP) += hci_bcsp.o +hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o +hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o hci_uart-objs := $(hci_uart-y) include $(TOPDIR)/Rules.make diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index 296d674bc38a..67dcad9138da 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -60,7 +60,7 @@ MODULE_PARM(irq_mask, "i"); MODULE_PARM(irq_list, "1-4i"); MODULE_AUTHOR("Marcel Holtmann "); -MODULE_DESCRIPTION("BlueZ driver for the Anycom BlueCard (LSE039/LSE041)"); +MODULE_DESCRIPTION("Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)"); MODULE_LICENSE("GPL"); @@ -396,7 +396,7 @@ static void bluecard_receive(bluecard_info_t *info, unsigned int offset) if (info->rx_skb == NULL) { info->rx_state = RECV_WAIT_PACKET_TYPE; info->rx_count = 0; - if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { + if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { printk(KERN_WARNING "bluecard_cs: Can't allocate mem for new packet.\n"); return; } @@ -571,7 +571,7 @@ static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud) /* Ericsson baud rate command */ unsigned char cmd[] = { HCI_COMMAND_PKT, 0x09, 0xfc, 0x01, 0x03 }; - if (!(skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { + if (!(skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { printk(KERN_WARNING "bluecard_cs: Can't allocate mem for new packet.\n"); return -1; } diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 517fd86199a4..460652790b07 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -72,7 +72,7 @@ MODULE_PARM(irq_mask, "i"); MODULE_PARM(irq_list, "1-4i"); MODULE_AUTHOR("Marcel Holtmann , Jose Orlando Pereira "); -MODULE_DESCRIPTION("BlueZ driver for the 3Com Bluetooth PCMCIA card"); +MODULE_DESCRIPTION("Bluetooth driver for the 3Com Bluetooth PCMCIA card"); MODULE_LICENSE("GPL"); @@ -264,7 +264,7 @@ static void bt3c_receive(bt3c_info_t *info) if (info->rx_skb == NULL) { info->rx_state = RECV_WAIT_PACKET_TYPE; info->rx_count = 0; - if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { + if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { printk(KERN_WARNING "bt3c_cs: Can't allocate mem for new packet.\n"); return; } diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 80df236ec1dc..63de1e3fc4ac 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -66,7 +66,7 @@ MODULE_PARM(irq_mask, "i"); MODULE_PARM(irq_list, "1-4i"); MODULE_AUTHOR("Marcel Holtmann "); -MODULE_DESCRIPTION("BlueZ driver for Nokia Connectivity Card DTL-1"); +MODULE_DESCRIPTION("Bluetooth driver for Nokia Connectivity Card DTL-1"); MODULE_LICENSE("GPL"); @@ -238,7 +238,7 @@ static void dtl1_receive(dtl1_info_t *info) /* Allocate packet */ if (info->rx_skb == NULL) - if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { + if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { printk(KERN_WARNING "dtl1_cs: Can't allocate mem for new packet.\n"); info->rx_state = RECV_WAIT_NSH; info->rx_count = NSHL; @@ -433,7 +433,7 @@ static int dtl1_hci_send_frame(struct sk_buff *skb) nsh.zero = 0; nsh.len = skb->len; - s = bluez_skb_alloc(NSHL + skb->len + 1, GFP_ATOMIC); + s = bt_skb_alloc(NSHL + skb->len + 1, GFP_ATOMIC); skb_reserve(s, NSHL); memcpy(skb_put(s, skb->len), skb->data, skb->len); if (skb->len & 0x0001) diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c index b3f3c71c8287..bfd5401987ec 100644 --- a/drivers/bluetooth/hci_bcsp.c +++ b/drivers/bluetooth/hci_bcsp.c @@ -57,7 +57,7 @@ #include "hci_uart.h" #include "hci_bcsp.h" -#ifndef HCI_UART_DEBUG +#ifndef CONFIG_BT_HCIUART_DEBUG #undef BT_DBG #define BT_DBG( A... ) #undef BT_DMP @@ -176,7 +176,7 @@ static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data, u8 hdr[4], chan; int rel, i; -#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC +#ifdef CONFIG_BT_HCIUART_BCSP_TXCRC u16 BCSP_CRC_INIT(bcsp_txmsg_crc); #endif @@ -228,7 +228,7 @@ static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data, BT_DBG("Sending packet with seqno %u", bcsp->msgq_txseq); bcsp->msgq_txseq = ++(bcsp->msgq_txseq) & 0x07; } -#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC +#ifdef CONFIG_BT_HCIUART_BCSP_TXCRC hdr[0] |= 0x40; #endif @@ -240,7 +240,7 @@ static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data, /* Put BCSP header */ for (i = 0; i < 4; i++) { bcsp_slip_one_byte(nskb, hdr[i]); -#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC +#ifdef CONFIG_BT_HCIUART_BCSP_TXCRC bcsp_crc_update(&bcsp_txmsg_crc, hdr[i]); #endif } @@ -248,12 +248,12 @@ static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data, /* Put payload */ for (i = 0; i < len; i++) { bcsp_slip_one_byte(nskb, data[i]); -#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC +#ifdef CONFIG_BT_HCIUART_BCSP_TXCRC bcsp_crc_update(&bcsp_txmsg_crc, data[i]); #endif } -#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC +#ifdef CONFIG_BT_HCIUART_BCSP_TXCRC /* Put CRC */ bcsp_txmsg_crc = bcsp_crc_reverse(bcsp_txmsg_crc); bcsp_slip_one_byte(nskb, (u8) ((bcsp_txmsg_crc >> 8) & 0x00ff)); @@ -611,7 +611,7 @@ static int bcsp_recv(struct hci_uart *hu, void *data, int count) * Allocate packet. Max len of a BCSP pkt= * 0xFFF (payload) +4 (header) +2 (crc) */ - bcsp->rx_skb = bluez_skb_alloc(0x1005, GFP_ATOMIC); + bcsp->rx_skb = bt_skb_alloc(0x1005, GFP_ATOMIC); if (!bcsp->rx_skb) { BT_ERR("Can't allocate mem for new packet"); bcsp->rx_state = BCSP_W4_PKT_DELIMITER; diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index 0e2a25e0526e..b1ba694cf524 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -23,7 +23,7 @@ */ /* - * BlueZ HCI UART(H4) protocol. + * Bluetooth HCI UART(H4) protocol. * * $Id: hci_h4.c,v 1.3 2002/09/09 01:17:32 maxk Exp $ */ @@ -56,7 +56,7 @@ #include "hci_uart.h" #include "hci_h4.h" -#ifndef HCI_UART_DEBUG +#ifndef CONFIG_BT_HCIUART_DEBUG #undef BT_DBG #define BT_DBG( A... ) #undef BT_DMP @@ -238,7 +238,7 @@ static int h4_recv(struct hci_uart *hu, void *data, int count) ptr++; count--; /* Allocate packet */ - h4->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); + h4->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); if (!h4->rx_skb) { BT_ERR("Can't allocate mem for new packet"); h4->rx_state = H4_W4_PACKET_TYPE; diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 067a25c7fc1f..e5196a92b7ba 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -23,7 +23,7 @@ */ /* - * BlueZ HCI UART driver. + * Bluetooth HCI UART driver. * * $Id: hci_ldisc.c,v 1.5 2002/10/02 18:37:20 maxk Exp $ */ @@ -55,7 +55,7 @@ #include #include "hci_uart.h" -#ifndef HCI_UART_DEBUG +#ifndef CONFIG_BT_HCIUART_DEBUG #undef BT_DBG #define BT_DBG( A... ) #undef BT_DMP @@ -507,11 +507,11 @@ static unsigned int hci_uart_tty_poll(struct tty_struct *tty, struct file *filp, return 0; } -#ifdef CONFIG_BLUEZ_HCIUART_H4 +#ifdef CONFIG_BT_HCIUART_H4 int h4_init(void); int h4_deinit(void); #endif -#ifdef CONFIG_BLUEZ_HCIUART_BCSP +#ifdef CONFIG_BT_HCIUART_BCSP int bcsp_init(void); int bcsp_deinit(void); #endif @@ -521,7 +521,7 @@ int __init hci_uart_init(void) static struct tty_ldisc hci_uart_ldisc; int err; - BT_INFO("BlueZ HCI UART driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", + BT_INFO("Bluetooth HCI UART driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", VERSION); BT_INFO("Written 2000,2001 by Maxim Krasnyansky "); @@ -545,10 +545,10 @@ int __init hci_uart_init(void) return err; } -#ifdef CONFIG_BLUEZ_HCIUART_H4 +#ifdef CONFIG_BT_HCIUART_H4 h4_init(); #endif -#ifdef CONFIG_BLUEZ_HCIUART_BCSP +#ifdef CONFIG_BT_HCIUART_BCSP bcsp_init(); #endif @@ -559,10 +559,10 @@ void hci_uart_cleanup(void) { int err; -#ifdef CONFIG_BLUEZ_HCIUART_H4 +#ifdef CONFIG_BT_HCIUART_H4 h4_deinit(); #endif -#ifdef CONFIG_BLUEZ_HCIUART_BCSP +#ifdef CONFIG_BT_HCIUART_BCSP bcsp_deinit(); #endif @@ -575,5 +575,5 @@ module_init(hci_uart_init); module_exit(hci_uart_cleanup); MODULE_AUTHOR("Maxim Krasnyansky "); -MODULE_DESCRIPTION("BlueZ HCI UART driver ver " VERSION); +MODULE_DESCRIPTION("Bluetooth HCI UART driver ver " VERSION); MODULE_LICENSE("GPL"); diff --git a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c index a94786f7d6ba..65248e1df44f 100644 --- a/drivers/bluetooth/hci_usb.c +++ b/drivers/bluetooth/hci_usb.c @@ -23,7 +23,7 @@ */ /* - * BlueZ HCI USB driver. + * Bluetooth HCI USB driver. * Based on original USB Bluetooth driver for Linux kernel * Copyright (c) 2000 Greg Kroah-Hartman * Copyright (c) 2000 Mark Douglas Corner @@ -59,14 +59,14 @@ #define HCI_MAX_PENDING (HCI_MAX_BULK_RX + HCI_MAX_BULK_TX + 1) -#ifndef HCI_USB_DEBUG +#ifndef CONFIG_BT_HCIUSB_DEBUG #undef BT_DBG #define BT_DBG( A... ) #undef BT_DMP #define BT_DMP( A... ) #endif -#ifndef CONFIG_BLUEZ_USB_ZERO_PACKET +#ifndef CONFIG_BT_USB_ZERO_PACKET #undef USB_ZERO_PACKET #define USB_ZERO_PACKET 0 #endif @@ -167,7 +167,7 @@ static int hci_usb_rx_submit(struct hci_usb *husb, struct urb *urb) size = HCI_MAX_FRAME_SIZE; - if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC))) { + if (!(skb = bt_skb_alloc(size, GFP_ATOMIC))) { usb_free_urb(urb); return -ENOMEM; } @@ -465,7 +465,7 @@ static void hci_usb_interrupt(struct urb *urb) if (count > len) goto bad_len; - skb = bluez_skb_alloc(len, GFP_ATOMIC); + skb = bt_skb_alloc(len, GFP_ATOMIC); if (!skb) { BT_ERR("%s no memory for event packet", husb->hdev.name); goto done; @@ -569,7 +569,7 @@ static void hci_usb_rx_complete(struct urb *urb) if (count != size) { BT_ERR("%s corrupted ACL packet: count %d, dlen %d", husb->hdev.name, count, dlen); - bluez_dump("hci_usb", skb->data, count); + bt_dump("hci_usb", skb->data, count); husb->hdev.stat.err_rx++; goto resubmit; } @@ -781,7 +781,7 @@ int hci_usb_init(void) { int err; - BT_INFO("BlueZ HCI USB driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", + BT_INFO("Bluetooth HCI USB driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", VERSION); BT_INFO("Written 2000,2001 by Maxim Krasnyansky "); @@ -800,5 +800,5 @@ module_init(hci_usb_init); module_exit(hci_usb_cleanup); MODULE_AUTHOR("Maxim Krasnyansky "); -MODULE_DESCRIPTION("BlueZ HCI USB driver ver " VERSION); +MODULE_DESCRIPTION("Bluetooth HCI USB driver ver " VERSION); MODULE_LICENSE("GPL"); diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index a3ca871e2049..aab1aca475d7 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -23,7 +23,7 @@ */ /* - * BlueZ HCI virtual device driver. + * Bluetooth HCI virtual device driver. * * $Id: hci_vhci.c,v 1.3 2002/04/17 17:37:20 maxk Exp $ */ @@ -136,7 +136,7 @@ static inline ssize_t hci_vhci_get_user(struct hci_vhci_struct *hci_vhci, const if (count > HCI_MAX_FRAME_SIZE) return -EINVAL; - if (!(skb = bluez_skb_alloc(count, GFP_KERNEL))) + if (!(skb = bt_skb_alloc(count, GFP_KERNEL))) return -ENOMEM; if (copy_from_user(skb_put(skb, count), buf, count)) { @@ -331,7 +331,7 @@ static struct miscdevice hci_vhci_miscdev= int __init hci_vhci_init(void) { - BT_INFO("BlueZ VHCI driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", + BT_INFO("Bluetooth VHCI driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", VERSION); BT_INFO("Written 2000,2001 by Maxim Krasnyansky "); @@ -352,5 +352,5 @@ module_init(hci_vhci_init); module_exit(hci_vhci_cleanup); MODULE_AUTHOR("Maxim Krasnyansky "); -MODULE_DESCRIPTION("BlueZ VHCI driver ver " VERSION); +MODULE_DESCRIPTION("Bluetooth VHCI driver ver " VERSION); MODULE_LICENSE("GPL"); diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 3253f7e9e06a..a7202d48b57c 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -40,7 +40,7 @@ #endif /* Reserv for core and drivers use */ -#define BLUEZ_SKB_RESERVE 8 +#define BT_SKB_RESERVE 8 #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) @@ -57,29 +57,12 @@ #define SOL_SCO 17 #define SOL_RFCOMM 18 -/* Debugging */ -#ifdef CONFIG_BLUEZ_DEBUG - -#define HCI_CORE_DEBUG 1 -#define HCI_SOCK_DEBUG 1 -#define HCI_UART_DEBUG 1 -#define HCI_USB_DEBUG 1 -//#define HCI_DATA_DUMP 1 - -#define L2CAP_DEBUG 1 -#define SCO_DEBUG 1 -#define AF_BLUETOOTH_DEBUG 1 - -#endif /* CONFIG_BLUEZ_DEBUG */ - -extern void bluez_dump(char *pref, __u8 *buf, int count); - #define BT_INFO(fmt, arg...) printk(KERN_INFO fmt "\n" , ## arg) #define BT_DBG(fmt, arg...) printk(KERN_INFO "%s: " fmt "\n" , __FUNCTION__ , ## arg) #define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __FUNCTION__ , ## arg) #ifdef HCI_DATA_DUMP -#define BT_DMP(buf, len) bluez_dump(__FUNCTION__, buf, len) +#define BT_DMP(buf, len) bt_dump(__FUNCTION__, buf, len) #else #define BT_DMP(D...) #endif @@ -127,9 +110,9 @@ bdaddr_t *strtoba(char *str); /* Common socket structures and functions */ -#define bluez_sk(__sk) ((struct bluez_sock *) __sk) +#define bt_sk(__sk) ((struct bt_sock *) __sk) -struct bluez_sock { +struct bt_sock { struct sock sk; bdaddr_t src; bdaddr_t dst; @@ -137,48 +120,48 @@ struct bluez_sock { struct sock *parent; }; -struct bluez_sock_list { +struct bt_sock_list { struct sock *head; rwlock_t lock; }; -int bluez_sock_register(int proto, struct net_proto_family *ops); -int bluez_sock_unregister(int proto); -struct sock *bluez_sock_alloc(struct socket *sock, int proto, int pi_size, int prio); -void bluez_sock_link(struct bluez_sock_list *l, struct sock *s); -void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *s); -int bluez_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm); -uint bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait); -int bluez_sock_w4_connect(struct sock *sk, int flags); +int bt_sock_register(int proto, struct net_proto_family *ops); +int bt_sock_unregister(int proto); +struct sock *bt_sock_alloc(struct socket *sock, int proto, int pi_size, int prio); +void bt_sock_link(struct bt_sock_list *l, struct sock *s); +void bt_sock_unlink(struct bt_sock_list *l, struct sock *s); +int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm); +uint bt_sock_poll(struct file * file, struct socket *sock, poll_table *wait); +int bt_sock_w4_connect(struct sock *sk, int flags); -void bluez_accept_enqueue(struct sock *parent, struct sock *sk); -struct sock *bluez_accept_dequeue(struct sock *parent, struct socket *newsock); +void bt_accept_enqueue(struct sock *parent, struct sock *sk); +struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock); /* Skb helpers */ -struct bluez_skb_cb { - int incomming; +struct bt_skb_cb { + int incoming; }; -#define bluez_cb(skb) ((struct bluez_skb_cb *)(skb->cb)) +#define bt_cb(skb) ((struct bt_skb_cb *)(skb->cb)) -static inline struct sk_buff *bluez_skb_alloc(unsigned int len, int how) +static inline struct sk_buff *bt_skb_alloc(unsigned int len, int how) { struct sk_buff *skb; - if ((skb = alloc_skb(len + BLUEZ_SKB_RESERVE, how))) { - skb_reserve(skb, BLUEZ_SKB_RESERVE); - bluez_cb(skb)->incomming = 0; + if ((skb = alloc_skb(len + BT_SKB_RESERVE, how))) { + skb_reserve(skb, BT_SKB_RESERVE); + bt_cb(skb)->incoming = 0; } return skb; } -static inline struct sk_buff *bluez_skb_send_alloc(struct sock *sk, unsigned long len, +static inline struct sk_buff *bt_skb_send_alloc(struct sock *sk, unsigned long len, int nb, int *err) { struct sk_buff *skb; - if ((skb = sock_alloc_send_skb(sk, len + BLUEZ_SKB_RESERVE, nb, err))) { - skb_reserve(skb, BLUEZ_SKB_RESERVE); - bluez_cb(skb)->incomming = 0; + if ((skb = sock_alloc_send_skb(sk, len + BT_SKB_RESERVE, nb, err))) { + skb_reserve(skb, BT_SKB_RESERVE); + bt_cb(skb)->incoming = 0; } return skb; @@ -193,11 +176,8 @@ static inline int skb_frags_no(struct sk_buff *skb) return n; } -int hci_core_init(void); -int hci_core_cleanup(void); -int hci_sock_init(void); -int hci_sock_cleanup(void); +void bt_dump(char *pref, __u8 *buf, int count); -int bterr(__u16 code); +int bt_err(__u16 code); #endif /* __BLUETOOTH_H */ diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index b58ebef5397b..d67d9721e091 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -582,7 +582,7 @@ typedef struct { } __attribute__ ((packed)) evt_read_remote_version_complete; #define EVT_READ_REMOTE_VERSION_COMPLETE_SIZE 8 -/* Internal events generated by BlueZ stack */ +/* Internal events generated by Bluetooth stack */ #define EVT_STACK_INTERNAL 0xfd typedef struct { __u16 type; diff --git a/net/Makefile b/net/Makefile index 6cc376aaebf1..4d09f62752fa 100644 --- a/net/Makefile +++ b/net/Makefile @@ -27,7 +27,7 @@ obj-$(CONFIG_NETROM) += netrom/ obj-$(CONFIG_ROSE) += rose/ obj-$(CONFIG_AX25) += ax25/ obj-$(CONFIG_IRDA) += irda/ -obj-$(CONFIG_BLUEZ) += bluetooth/ +obj-$(CONFIG_BT) += bluetooth/ obj-$(CONFIG_SUNRPC) += sunrpc/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_DECNET) += decnet/ diff --git a/net/bluetooth/Config.help b/net/bluetooth/Config.help index e8553f4c1e4e..d69299604b51 100644 --- a/net/bluetooth/Config.help +++ b/net/bluetooth/Config.help @@ -1,5 +1,5 @@ Bluetooth subsystem support -CONFIG_BLUEZ +CONFIG_BT Bluetooth is low-cost, low-power, short-range wireless technology. It was designed as a replacement for cables and other short-range technologies like IrDA. Bluetooth operates in personal area range @@ -25,7 +25,7 @@ CONFIG_BLUEZ If you want to compile Bluetooth Core as module (bluetooth.o) say M here. L2CAP protocol support -CONFIG_BLUEZ_L2CAP +CONFIG_BT_L2CAP L2CAP (Logical Link Control and Adaptation Protocol) provides connection oriented and connection-less data transport. L2CAP support is required for most Bluetooth applications. @@ -34,7 +34,7 @@ CONFIG_BLUEZ_L2CAP compile it as module (l2cap.o). SCO links support -CONFIG_BLUEZ_SCO +CONFIG_BT_SCO SCO link provides voice transport over Bluetooth. SCO support is required for voice applications like Headset and Audio. diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index 13fc61d2630d..b54e5162b731 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -4,11 +4,11 @@ export-objs := syms.o -obj-$(CONFIG_BLUEZ) += bluetooth.o -obj-$(CONFIG_BLUEZ_L2CAP) += l2cap.o -obj-$(CONFIG_BLUEZ_SCO) += sco.o -obj-$(CONFIG_BLUEZ_RFCOMM) += rfcomm/ -obj-$(CONFIG_BLUEZ_BNEP) += bnep/ +obj-$(CONFIG_BT) += bluetooth.o +obj-$(CONFIG_BT_L2CAP) += l2cap.o +obj-$(CONFIG_BT_SCO) += sco.o +obj-$(CONFIG_BT_RFCOMM) += rfcomm/ +obj-$(CONFIG_BT_BNEP) += bnep/ bluetooth-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o lib.o syms.o diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index e5b5e6b027fb..9ad9c4454f53 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -23,7 +23,7 @@ */ /* - * BlueZ Bluetooth address family and sockets. + * Bluetooth address family and sockets. * * $Id: af_bluetooth.c,v 1.3 2002/04/17 17:37:15 maxk Exp $ */ @@ -51,66 +51,66 @@ #include -#ifndef AF_BLUETOOTH_DEBUG +#ifndef CONFIG_BT_SOCK_DEBUG #undef BT_DBG #define BT_DBG( A... ) #endif /* Bluetooth sockets */ -#define BLUEZ_MAX_PROTO 5 -static struct net_proto_family *bluez_proto[BLUEZ_MAX_PROTO]; +#define BT_MAX_PROTO 5 +static struct net_proto_family *bt_proto[BT_MAX_PROTO]; -static kmem_cache_t *bluez_sock_cache; +static kmem_cache_t *bt_sock_cache; -int bluez_sock_register(int proto, struct net_proto_family *ops) +int bt_sock_register(int proto, struct net_proto_family *ops) { - if (proto >= BLUEZ_MAX_PROTO) + if (proto >= BT_MAX_PROTO) return -EINVAL; - if (bluez_proto[proto]) + if (bt_proto[proto]) return -EEXIST; - bluez_proto[proto] = ops; + bt_proto[proto] = ops; return 0; } -int bluez_sock_unregister(int proto) +int bt_sock_unregister(int proto) { - if (proto >= BLUEZ_MAX_PROTO) + if (proto >= BT_MAX_PROTO) return -EINVAL; - if (!bluez_proto[proto]) + if (!bt_proto[proto]) return -ENOENT; - bluez_proto[proto] = NULL; + bt_proto[proto] = NULL; return 0; } -static int bluez_sock_create(struct socket *sock, int proto) +static int bt_sock_create(struct socket *sock, int proto) { - if (proto > BLUEZ_MAX_PROTO) + if (proto > BT_MAX_PROTO) return -EINVAL; #if defined(CONFIG_KMOD) - if (!bluez_proto[proto]) { + if (!bt_proto[proto]) { char module_name[30]; sprintf(module_name, "bt-proto-%d", proto); request_module(module_name); } #endif - if (!bluez_proto[proto]) + if (!bt_proto[proto]) return -ENOENT; - return bluez_proto[proto]->create(sock, proto); + return bt_proto[proto]->create(sock, proto); } -struct sock *bluez_sock_alloc(struct socket *sock, int proto, int pi_size, int prio) +struct sock *bt_sock_alloc(struct socket *sock, int proto, int pi_size, int prio) { struct sock *sk; void *pi; - sk = sk_alloc(PF_BLUETOOTH, prio, sizeof(struct bluez_sock), bluez_sock_cache); + sk = sk_alloc(PF_BLUETOOTH, prio, sizeof(struct bt_sock), bt_sock_cache); if (!sk) return NULL; @@ -125,7 +125,7 @@ struct sock *bluez_sock_alloc(struct socket *sock, int proto, int pi_size, int p } sock_init_data(sock, sk); - INIT_LIST_HEAD(&bluez_sk(sk)->accept_q); + INIT_LIST_HEAD(&bt_sk(sk)->accept_q); sk->zapped = 0; sk->protocol = proto; @@ -134,7 +134,7 @@ struct sock *bluez_sock_alloc(struct socket *sock, int proto, int pi_size, int p return sk; } -void bluez_sock_link(struct bluez_sock_list *l, struct sock *sk) +void bt_sock_link(struct bt_sock_list *l, struct sock *sk) { write_lock_bh(&l->lock); sk->next = l->head; @@ -143,7 +143,7 @@ void bluez_sock_link(struct bluez_sock_list *l, struct sock *sk) write_unlock_bh(&l->lock); } -void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *sk) +void bt_sock_unlink(struct bt_sock_list *l, struct sock *sk) { struct sock **skp; @@ -158,45 +158,45 @@ void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *sk) write_unlock_bh(&l->lock); } -void bluez_accept_enqueue(struct sock *parent, struct sock *sk) +void bt_accept_enqueue(struct sock *parent, struct sock *sk) { BT_DBG("parent %p, sk %p", parent, sk); sock_hold(sk); - list_add_tail(&bluez_sk(sk)->accept_q, &bluez_sk(parent)->accept_q); - bluez_sk(sk)->parent = parent; + list_add_tail(&bt_sk(sk)->accept_q, &bt_sk(parent)->accept_q); + bt_sk(sk)->parent = parent; parent->ack_backlog++; } -static void bluez_accept_unlink(struct sock *sk) +static void bt_accept_unlink(struct sock *sk) { BT_DBG("sk %p state %d", sk, sk->state); - list_del_init(&bluez_sk(sk)->accept_q); - bluez_sk(sk)->parent->ack_backlog--; - bluez_sk(sk)->parent = NULL; + list_del_init(&bt_sk(sk)->accept_q); + bt_sk(sk)->parent->ack_backlog--; + bt_sk(sk)->parent = NULL; sock_put(sk); } -struct sock *bluez_accept_dequeue(struct sock *parent, struct socket *newsock) +struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) { struct list_head *p, *n; struct sock *sk; BT_DBG("parent %p", parent); - list_for_each_safe(p, n, &bluez_sk(parent)->accept_q) { - sk = (struct sock *) list_entry(p, struct bluez_sock, accept_q); + list_for_each_safe(p, n, &bt_sk(parent)->accept_q) { + sk = (struct sock *) list_entry(p, struct bt_sock, accept_q); lock_sock(sk); if (sk->state == BT_CLOSED) { release_sock(sk); - bluez_accept_unlink(sk); + bt_accept_unlink(sk); continue; } if (sk->state == BT_CONNECTED || !newsock) { - bluez_accept_unlink(sk); + bt_accept_unlink(sk); if (newsock) sock_graft(sk, newsock); release_sock(sk); @@ -207,7 +207,7 @@ struct sock *bluez_accept_dequeue(struct sock *parent, struct socket *newsock) return NULL; } -int bluez_sock_recvmsg(struct kiocb *iocb, struct socket *sock, +int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm) { int noblock = flags & MSG_DONTWAIT; @@ -242,7 +242,7 @@ int bluez_sock_recvmsg(struct kiocb *iocb, struct socket *sock, return err ? : copied; } -unsigned int bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait) +unsigned int bt_sock_poll(struct file * file, struct socket *sock, poll_table *wait) { struct sock *sk = sock->sk; unsigned int mask; @@ -259,7 +259,7 @@ unsigned int bluez_sock_poll(struct file * file, struct socket *sock, poll_table mask |= POLLHUP; if (!skb_queue_empty(&sk->receive_queue) || - !list_empty(&bluez_sk(sk)->accept_q) || + !list_empty(&bt_sk(sk)->accept_q) || (sk->shutdown & RCV_SHUTDOWN)) mask |= POLLIN | POLLRDNORM; @@ -277,7 +277,7 @@ unsigned int bluez_sock_poll(struct file * file, struct socket *sock, poll_table return mask; } -int bluez_sock_w4_connect(struct sock *sk, int flags) +int bt_sock_w4_connect(struct sock *sk, int flags) { DECLARE_WAITQUEUE(wait, current); long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); @@ -316,50 +316,51 @@ int bluez_sock_w4_connect(struct sock *sk, int flags) return err; } -struct net_proto_family bluez_sock_family_ops = +struct net_proto_family bt_sock_family_ops = { - PF_BLUETOOTH, bluez_sock_create + PF_BLUETOOTH, bt_sock_create }; -static int __init bluez_init(void) +extern int hci_sock_init(void); +extern int hci_sock_cleanup(void); + +static int __init bt_init(void) { - BT_INFO("BlueZ Core ver %s Copyright (C) 2000,2001 Qualcomm Inc", + BT_INFO("Bluetooth Core ver %s Copyright (C) 2000,2001 Qualcomm Inc", VERSION); BT_INFO("Written 2000,2001 by Maxim Krasnyansky "); proc_mkdir("bluetooth", NULL); /* Init socket cache */ - bluez_sock_cache = kmem_cache_create("bluez_sock", - sizeof(struct bluez_sock), 0, + bt_sock_cache = kmem_cache_create("bt_sock", + sizeof(struct bt_sock), 0, SLAB_HWCACHE_ALIGN, 0, 0); - if (!bluez_sock_cache) { - BT_ERR("BlueZ socket cache creation failed"); + if (!bt_sock_cache) { + BT_ERR("Bluetooth socket cache creation failed"); return -ENOMEM; } - sock_register(&bluez_sock_family_ops); + sock_register(&bt_sock_family_ops); - hci_core_init(); hci_sock_init(); return 0; } -static void __exit bluez_cleanup(void) +static void __exit bt_cleanup(void) { hci_sock_cleanup(); - hci_core_cleanup(); sock_unregister(PF_BLUETOOTH); - kmem_cache_destroy(bluez_sock_cache); + kmem_cache_destroy(bt_sock_cache); remove_proc_entry("bluetooth", NULL); } -subsys_initcall(bluez_init); -module_exit(bluez_cleanup); +subsys_initcall(bt_init); +module_exit(bt_cleanup); MODULE_AUTHOR("Maxim Krasnyansky "); -MODULE_DESCRIPTION("BlueZ Core ver " VERSION); +MODULE_DESCRIPTION("Bluetooth Core ver " VERSION); MODULE_LICENSE("GPL"); diff --git a/net/bluetooth/bnep/Config.help b/net/bluetooth/bnep/Config.help index 64bafc9dc313..25fdd001eb42 100644 --- a/net/bluetooth/bnep/Config.help +++ b/net/bluetooth/bnep/Config.help @@ -1,5 +1,5 @@ BNEP protocol support -CONFIG_BLUEZ_BNEP +CONFIG_BT_BNEP BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet emulation layer on top of Bluetooth. BNEP is required for Bluetooth PAN (Personal Area Network). @@ -12,10 +12,10 @@ CONFIG_BLUEZ_BNEP compile it as module (bnep.o). BNEP multicast filter support -CONFIG_BLUEZ_BNEP_MC_FILTER +CONFIG_BT_BNEP_MC_FILTER This option enables the multicast filter support for BNEP. BNEP protocol filter support -CONFIG_BLUEZ_BNEP_PROTO_FILTER +CONFIG_BT_BNEP_PROTO_FILTER This option enables the protocol filter support for BNEP. diff --git a/net/bluetooth/bnep/Config.in b/net/bluetooth/bnep/Config.in index 8de2ad3c951d..2e1fd13547ec 100644 --- a/net/bluetooth/bnep/Config.in +++ b/net/bluetooth/bnep/Config.in @@ -1,8 +1,8 @@ -dep_tristate 'BNEP protocol support' CONFIG_BLUEZ_BNEP $CONFIG_BLUEZ_L2CAP +dep_tristate 'BNEP protocol support' CONFIG_BT_BNEP $CONFIG_BT_L2CAP -if [ "$CONFIG_BLUEZ_BNEP" != "n" ]; then - bool ' Multicast filter support' CONFIG_BLUEZ_BNEP_MC_FILTER - bool ' Protocol filter support' CONFIG_BLUEZ_BNEP_PROTO_FILTER +if [ "$CONFIG_BT_BNEP" != "n" ]; then + bool ' Multicast filter support' CONFIG_BT_BNEP_MC_FILTER + bool ' Protocol filter support' CONFIG_BT_BNEP_PROTO_FILTER fi diff --git a/net/bluetooth/bnep/Makefile b/net/bluetooth/bnep/Makefile index 7baf319e271e..f424217b0d1e 100644 --- a/net/bluetooth/bnep/Makefile +++ b/net/bluetooth/bnep/Makefile @@ -2,7 +2,7 @@ # Makefile for the Linux Bluetooth BNEP layer. # -obj-$(CONFIG_BLUEZ_BNEP) += bnep.o +obj-$(CONFIG_BT_BNEP) += bnep.o bnep-objs := core.o sock.o netdev.o crc32.o diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index 6b79efdd8c68..fb9fae8c9710 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -58,7 +58,7 @@ #include "bnep.h" -#ifndef CONFIG_BLUEZ_BNEP_DEBUG +#ifndef CONFIG_BT_BNEP_DEBUG #undef BT_DBG #define BT_DBG(D...) #endif @@ -144,7 +144,7 @@ static int bnep_ctrl_set_netfilter(struct bnep_session *s, struct sk_buff *skb) BT_DBG("filter len %d", n); -#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER +#ifdef CONFIG_BT_BNEP_PROTO_FILTER n /= 4; if (n <= BNEP_MAX_PROTO_FILTERS) { struct bnep_proto_filter *f = s->proto_filter; @@ -186,7 +186,7 @@ static int bnep_ctrl_set_mcfilter(struct bnep_session *s, struct sk_buff *skb) BT_DBG("filter len %d", n); -#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER +#ifdef CONFIG_BT_BNEP_MC_FILTER n /= (ETH_ALEN * 2); if (n > 0) { @@ -538,8 +538,8 @@ int bnep_add_connection(struct bnep_conadd_req *req, struct socket *sock) BT_DBG(""); - baswap((void *) dst, &bluez_sk(sock->sk)->dst); - baswap((void *) src, &bluez_sk(sock->sk)->src); + baswap((void *) dst, &bt_sk(sock->sk)->dst); + baswap((void *) src, &bt_sk(sock->sk)->src); s = kmalloc(sizeof(struct bnep_session), GFP_KERNEL); if (!s) @@ -572,12 +572,12 @@ int bnep_add_connection(struct bnep_conadd_req *req, struct socket *sock) s->msg.msg_flags = MSG_NOSIGNAL; -#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER +#ifdef CONFIG_BT_BNEP_MC_FILTER /* Set default mc filter */ set_bit(bnep_mc_hash(dev->broadcast), (ulong *) &s->mc_filter); #endif -#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER +#ifdef CONFIG_BT_BNEP_PROTO_FILTER /* Set default protocol filter */ /* (IPv4, ARP) */ diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c index bc17e026aefa..71217edf3566 100644 --- a/net/bluetooth/bnep/netdev.c +++ b/net/bluetooth/bnep/netdev.c @@ -46,7 +46,7 @@ #include "bnep.h" -#ifndef CONFIG_BLUEZ_BNEP_DEBUG +#ifndef CONFIG_BT_BNEP_DEBUG #undef BT_DBG #define BT_DBG( A... ) #endif @@ -73,7 +73,7 @@ static struct net_device_stats *bnep_net_get_stats(struct net_device *dev) static void bnep_net_set_mc_list(struct net_device *dev) { -#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER +#ifdef CONFIG_BT_BNEP_MC_FILTER struct bnep_session *s = dev->priv; struct sock *sk = s->sock->sk; struct bnep_set_filter_req *r; @@ -143,7 +143,7 @@ static int bnep_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return -EINVAL; } -#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER +#ifdef CONFIG_BT_BNEP_MC_FILTER static inline int bnep_net_mc_filter(struct sk_buff *skb, struct bnep_session *s) { struct ethhdr *eh = (void *) skb->data; @@ -154,7 +154,7 @@ static inline int bnep_net_mc_filter(struct sk_buff *skb, struct bnep_session *s } #endif -#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER +#ifdef CONFIG_BT_BNEP_PROTO_FILTER /* Determine ether protocol. Based on eth_type_trans. */ static inline u16 bnep_net_eth_proto(struct sk_buff *skb) { @@ -192,14 +192,14 @@ static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev) BT_DBG("skb %p, dev %p", skb, dev); -#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER +#ifdef CONFIG_BT_BNEP_MC_FILTER if (bnep_net_mc_filter(skb, s)) { kfree_skb(skb); return 0; } #endif -#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER +#ifdef CONFIG_BT_BNEP_PROTO_FILTER if (bnep_net_proto_filter(skb, s)) { kfree_skb(skb); return 0; diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c index c737acbea6aa..dc9f55584e4d 100644 --- a/net/bluetooth/bnep/sock.c +++ b/net/bluetooth/bnep/sock.c @@ -50,7 +50,7 @@ #include "bnep.h" -#ifndef CONFIG_BLUEZ_BNEP_DEBUG +#ifndef CONFIG_BT_BNEP_DEBUG #undef BT_DBG #define BT_DBG( A... ) #endif @@ -198,7 +198,7 @@ static int bnep_sock_create(struct socket *sock, int protocol) if (sock->type != SOCK_RAW) return -ESOCKTNOSUPPORT; - if (!(sk = bluez_sock_alloc(sock, PF_BLUETOOTH, 0, GFP_KERNEL))) + if (!(sk = bt_sock_alloc(sock, PF_BLUETOOTH, 0, GFP_KERNEL))) return -ENOMEM; sock->ops = &bnep_sock_ops; @@ -219,13 +219,13 @@ static struct net_proto_family bnep_sock_family_ops = { int bnep_sock_init(void) { - bluez_sock_register(BTPROTO_BNEP, &bnep_sock_family_ops); + bt_sock_register(BTPROTO_BNEP, &bnep_sock_family_ops); return 0; } int bnep_sock_cleanup(void) { - if (bluez_sock_unregister(BTPROTO_BNEP)) + if (bt_sock_unregister(BTPROTO_BNEP)) BT_ERR("Can't unregister BNEP socket"); return 0; } diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 536402c9581a..64d3e5cf31e5 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -52,7 +52,7 @@ #include #include -#ifndef HCI_CORE_DEBUG +#ifndef CONFIG_BT_HCI_CORE_DEBUG #undef BT_DBG #define BT_DBG( A... ) #endif diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f1485ad896bd..f013113f34ec 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -23,7 +23,7 @@ */ /* - * BlueZ HCI Core. + * Bluetooth HCI Core. * * $Id: hci_core.c,v 1.6 2002/04/17 17:37:16 maxk Exp $ */ @@ -53,7 +53,7 @@ #include #include -#ifndef HCI_CORE_DEBUG +#ifndef CONFIG_BT_HCI_CORE_DEBUG #undef BT_DBG #define BT_DBG( A... ) #endif @@ -168,7 +168,7 @@ static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, switch (hdev->req_status) { case HCI_REQ_DONE: - err = -bterr(hdev->req_result); + err = -bt_err(hdev->req_result); break; case HCI_REQ_CANCELED: @@ -877,7 +877,7 @@ int hci_recv_frame(struct sk_buff *skb) BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len); /* Incomming skb */ - bluez_cb(skb)->incomming = 1; + bt_cb(skb)->incoming = 1; /* Time stamp */ do_gettimeofday(&skb->stamp); @@ -1001,7 +1001,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *p BT_DBG("%s ogf 0x%x ocf 0x%x plen %d", hdev->name, ogf, ocf, plen); - if (!(skb = bluez_skb_alloc(len, GFP_ATOMIC))) { + if (!(skb = bt_skb_alloc(len, GFP_ATOMIC))) { BT_ERR("%s Can't allocate memory for HCI command", hdev->name); return -ENOMEM; } @@ -1250,7 +1250,7 @@ static void hci_tx_task(unsigned long arg) read_unlock(&hci_task_lock); } -/* ----- HCI RX task (incomming data proccessing) ----- */ +/* ----- HCI RX task (incoming data proccessing) ----- */ /* ACL data packet */ static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb) @@ -1406,15 +1406,3 @@ static void hci_cmd_task(unsigned long arg) } } } - -/* ---- Initialization ---- */ - -int hci_core_init(void) -{ - return 0; -} - -int hci_core_cleanup(void) -{ - return 0; -} diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 9445e4543416..c9303bbd8cde 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -52,7 +52,7 @@ #include #include -#ifndef HCI_CORE_DEBUG +#ifndef CONFIG_BT_HCI_CORE_DEBUG #undef BT_DBG #define BT_DBG( A... ) #endif @@ -68,7 +68,7 @@ static void hci_cc_link_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb default: BT_DBG("%s Command complete: ogf LINK_CTL ocf %x", hdev->name, ocf); break; - }; + } } /* Command Complete OGF LINK_POLICY */ @@ -103,7 +103,7 @@ static void hci_cc_link_policy(struct hci_dev *hdev, __u16 ocf, struct sk_buff * BT_DBG("%s: Command complete: ogf LINK_POLICY ocf %x", hdev->name, ocf); break; - }; + } } /* Command Complete OGF HOST_CTL */ @@ -213,7 +213,7 @@ static void hci_cc_host_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb default: BT_DBG("%s Command complete: ogf HOST_CTL ocf %x", hdev->name, ocf); break; - }; + } } /* Command Complete OGF INFO_PARAM */ @@ -287,7 +287,7 @@ static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *s default: BT_DBG("%s Command complete: ogf INFO_PARAM ocf %x", hdev->name, ocf); break; - }; + } } /* Command Status OGF LINK_CTL */ @@ -376,7 +376,7 @@ static void hci_cs_link_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status) BT_DBG("%s Command status: ogf LINK_CTL ocf %x status %d", hdev->name, ocf, status); break; - }; + } } /* Command Status OGF LINK_POLICY */ @@ -388,7 +388,7 @@ static void hci_cs_link_policy(struct hci_dev *hdev, __u16 ocf, __u8 status) default: BT_DBG("%s Command status: ogf HOST_POLICY ocf %x", hdev->name, ocf); break; - }; + } } /* Command Status OGF HOST_CTL */ @@ -400,7 +400,7 @@ static void hci_cs_host_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status) default: BT_DBG("%s Command status: ogf HOST_CTL ocf %x", hdev->name, ocf); break; - }; + } } /* Command Status OGF INFO_PARAM */ @@ -412,7 +412,7 @@ static void hci_cs_info_param(struct hci_dev *hdev, __u16 ocf, __u8 status) default: BT_DBG("%s Command status: ogf INFO_PARAM ocf %x", hdev->name, ocf); break; - }; + } } /* Inquiry Complete */ @@ -849,7 +849,7 @@ void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) void *ptr; size = HCI_EVENT_HDR_SIZE + EVT_STACK_INTERNAL_SIZE + dlen; - skb = bluez_skb_alloc(size, GFP_ATOMIC); + skb = bt_skb_alloc(size, GFP_ATOMIC); if (!skb) return; diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 09f6f3247607..e9379c7f2014 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -23,7 +23,7 @@ */ /* - * BlueZ HCI socket layer. + * Bluetooth HCI socket layer. * * $Id: hci_sock.c,v 1.4 2002/04/18 22:26:14 maxk Exp $ */ @@ -53,7 +53,7 @@ #include #include -#ifndef HCI_SOCK_DEBUG +#ifndef CONFIG_BT_HCI_SOCK_DEBUG #undef BT_DBG #define BT_DBG( A... ) #endif @@ -79,7 +79,7 @@ static struct hci_sec_filter hci_sec_filter = { } }; -static struct bluez_sock_list hci_sk_list = { +static struct bt_sock_list hci_sk_list = { .lock = RW_LOCK_UNLOCKED }; @@ -144,7 +144,7 @@ static int hci_sock_release(struct socket *sock) if (!sk) return 0; - bluez_sock_unlink(&hci_sk_list, sk); + bt_sock_unlink(&hci_sk_list, sk); if (hdev) { atomic_dec(&hdev->promisc); @@ -310,7 +310,7 @@ static inline void hci_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_ __u32 mask = hci_pi(sk)->cmsg_mask; if (mask & HCI_CMSG_DIR) - put_cmsg(msg, SOL_HCI, HCI_CMSG_DIR, sizeof(int), &bluez_cb(skb)->incomming); + put_cmsg(msg, SOL_HCI, HCI_CMSG_DIR, sizeof(int), &bt_cb(skb)->incoming); if (mask & HCI_CMSG_TSTAMP) put_cmsg(msg, SOL_HCI, HCI_CMSG_TSTAMP, sizeof(skb->stamp), &skb->stamp); @@ -378,7 +378,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh goto done; } - if (!(skb = bluez_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err))) + if (!(skb = bt_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err))) goto done; if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { @@ -568,14 +568,14 @@ static int hci_sock_create(struct socket *sock, int protocol) sock->ops = &hci_sock_ops; - sk = bluez_sock_alloc(sock, protocol, sizeof(struct hci_pinfo), GFP_KERNEL); + sk = bt_sock_alloc(sock, protocol, sizeof(struct hci_pinfo), GFP_KERNEL); if (!sk) return -ENOMEM; sock->state = SS_UNCONNECTED; sk->state = BT_OPEN; - bluez_sock_link(&hci_sk_list, sk); + bt_sock_link(&hci_sk_list, sk); MOD_INC_USE_COUNT; return 0; @@ -627,7 +627,7 @@ struct notifier_block hci_sock_nblock = { int hci_sock_init(void) { - if (bluez_sock_register(BTPROTO_HCI, &hci_sock_family_ops)) { + if (bt_sock_register(BTPROTO_HCI, &hci_sock_family_ops)) { BT_ERR("Can't register HCI socket"); return -EPROTO; } @@ -638,7 +638,7 @@ int hci_sock_init(void) int hci_sock_cleanup(void) { - if (bluez_sock_unregister(BTPROTO_HCI)) + if (bt_sock_unregister(BTPROTO_HCI)) BT_ERR("Can't unregister HCI socket"); hci_unregister_notifier(&hci_sock_nblock); diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index b8bdf5978299..cb47e43234e9 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -23,7 +23,7 @@ */ /* - * BlueZ L2CAP core and sockets. + * Bluetooth L2CAP core and sockets. * * $Id: l2cap.c,v 1.15 2002/09/09 01:14:52 maxk Exp $ */ @@ -57,14 +57,14 @@ #include #include -#ifndef L2CAP_DEBUG +#ifndef CONFIG_BT_L2CAP_DEBUG #undef BT_DBG #define BT_DBG( A... ) #endif static struct proto_ops l2cap_sock_ops; -struct bluez_sock_list l2cap_sk_list = { +struct bt_sock_list l2cap_sk_list = { .lock = RW_LOCK_UNLOCKED }; @@ -188,8 +188,8 @@ static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, stru int l2cap_connect(struct sock *sk) { - bdaddr_t *src = &bluez_sk(sk)->src; - bdaddr_t *dst = &bluez_sk(sk)->dst; + bdaddr_t *src = &bt_sk(sk)->src; + bdaddr_t *dst = &bt_sk(sk)->dst; struct l2cap_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; @@ -248,7 +248,7 @@ static struct sock *__l2cap_get_sock_by_addr(__u16 psm, bdaddr_t *src) struct sock *sk; for (sk = l2cap_sk_list.head; sk; sk = sk->next) { if (l2cap_pi(sk)->psm == psm && - !bacmp(&bluez_sk(sk)->src, src)) + !bacmp(&bt_sk(sk)->src, src)) break; } return sk; @@ -267,11 +267,11 @@ static struct sock *__l2cap_get_sock_by_psm(int state, __u16 psm, bdaddr_t *src) if (l2cap_pi(sk)->psm == psm) { /* Exact match. */ - if (!bacmp(&bluez_sk(sk)->src, src)) + if (!bacmp(&bt_sk(sk)->src, src)) break; /* Closest match */ - if (!bacmp(&bluez_sk(sk)->src, BDADDR_ANY)) + if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) sk1 = sk; } } @@ -310,7 +310,7 @@ static void l2cap_sock_cleanup_listen(struct sock *parent) BT_DBG("parent %p", parent); /* Close not yet accepted channels */ - while ((sk = bluez_accept_dequeue(parent, NULL))) + while ((sk = bt_accept_dequeue(parent, NULL))) l2cap_sock_close(sk); parent->state = BT_CLOSED; @@ -328,7 +328,7 @@ static void l2cap_sock_kill(struct sock *sk) BT_DBG("sk %p state %d", sk, sk->state); /* Kill poor orphan */ - bluez_sock_unlink(&l2cap_sk_list, sk); + bt_sock_unlink(&l2cap_sk_list, sk); sk->dead = 1; sock_put(sk); } @@ -409,7 +409,7 @@ static struct sock *l2cap_sock_alloc(struct socket *sock, int proto, int prio) { struct sock *sk; - sk = bluez_sock_alloc(sock, proto, sizeof(struct l2cap_pinfo), prio); + sk = bt_sock_alloc(sock, proto, sizeof(struct l2cap_pinfo), prio); if (!sk) return NULL; @@ -421,7 +421,7 @@ static struct sock *l2cap_sock_alloc(struct socket *sock, int proto, int prio) l2cap_sock_init_timer(sk); - bluez_sock_link(&l2cap_sk_list, sk); + bt_sock_link(&l2cap_sk_list, sk); MOD_INC_USE_COUNT; return sk; @@ -471,7 +471,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_ err = -EADDRINUSE; } else { /* Save source address */ - bacpy(&bluez_sk(sk)->src, &la->l2_bdaddr); + bacpy(&bt_sk(sk)->src, &la->l2_bdaddr); l2cap_pi(sk)->psm = la->l2_psm; sk->state = BT_BOUND; } @@ -524,14 +524,14 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al } /* Set destination address and psm */ - bacpy(&bluez_sk(sk)->dst, &la->l2_bdaddr); + bacpy(&bt_sk(sk)->dst, &la->l2_bdaddr); l2cap_pi(sk)->psm = la->l2_psm; if ((err = l2cap_connect(sk))) goto done; wait: - err = bluez_sock_w4_connect(sk, flags); + err = bt_sock_w4_connect(sk, flags); done: release_sock(sk); @@ -586,7 +586,7 @@ int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags) /* Wait for an incoming connection. (wake-one). */ add_wait_queue_exclusive(sk->sleep, &wait); - while (!(nsk = bluez_accept_dequeue(sk, newsock))) { + while (!(nsk = bt_accept_dequeue(sk, newsock))) { set_current_state(TASK_INTERRUPTIBLE); if (!timeo) { err = -EAGAIN; @@ -633,9 +633,9 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l *len = sizeof(struct sockaddr_l2); if (peer) - bacpy(&la->l2_bdaddr, &bluez_sk(sk)->dst); + bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst); else - bacpy(&la->l2_bdaddr, &bluez_sk(sk)->src); + bacpy(&la->l2_bdaddr, &bt_sk(sk)->src); la->l2_psm = l2cap_pi(sk)->psm; return 0; @@ -892,7 +892,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so __l2cap_chan_link(l, sk); if (parent) - bluez_accept_enqueue(parent, sk); + bt_accept_enqueue(parent, sk); } /* Delete channel. @@ -900,7 +900,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so static void l2cap_chan_del(struct sock *sk, int err) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; - struct sock *parent = bluez_sk(sk)->parent; + struct sock *parent = bt_sk(sk)->parent; l2cap_sock_clear_timer(sk); @@ -954,7 +954,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) static void l2cap_chan_ready(struct sock *sk) { - struct sock *parent = bluez_sk(sk)->parent; + struct sock *parent = bt_sk(sk)->parent; BT_DBG("sk %p, parent %p", sk, parent); @@ -1019,7 +1019,7 @@ static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len) count = MIN(conn->mtu - hlen, len); - skb = bluez_skb_send_alloc(sk, hlen + count, + skb = bt_skb_send_alloc(sk, hlen + count, msg->msg_flags & MSG_DONTWAIT, &err); if (!skb) return err; @@ -1045,7 +1045,7 @@ static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len) while (len) { count = MIN(conn->mtu, len); - *frag = bluez_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err); + *frag = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err); if (!*frag) goto fail; @@ -1105,7 +1105,7 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen; count = MIN(conn->mtu, len); - skb = bluez_skb_alloc(count, GFP_ATOMIC); + skb = bt_skb_alloc(count, GFP_ATOMIC); if (!skb) return NULL; @@ -1131,7 +1131,7 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, while (len) { count = MIN(conn->mtu, len); - *frag = bluez_skb_alloc(count, GFP_ATOMIC); + *frag = bt_skb_alloc(count, GFP_ATOMIC); if (!*frag) goto fail; @@ -1374,8 +1374,8 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, hci_conn_hold(conn->hcon); l2cap_sock_init(sk, parent); - bacpy(&bluez_sk(sk)->src, conn->src); - bacpy(&bluez_sk(sk)->dst, conn->dst); + bacpy(&bt_sk(sk)->src, conn->src); + bacpy(&bt_sk(sk)->dst, conn->dst); l2cap_pi(sk)->psm = psm; l2cap_pi(sk)->dcid = scid; @@ -1785,10 +1785,10 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type) if (sk->state != BT_LISTEN) continue; - if (!bacmp(&bluez_sk(sk)->src, bdaddr)) { + if (!bacmp(&bt_sk(sk)->src, bdaddr)) { lm1 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode); exact++; - } else if (!bacmp(&bluez_sk(sk)->src, BDADDR_ANY)) + } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) lm2 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode); } read_unlock(&l2cap_sk_list.lock); @@ -1810,7 +1810,7 @@ static int l2cap_connect_cfm(struct hci_conn *hcon, __u8 status) if (conn) l2cap_conn_ready(conn); } else - l2cap_conn_del(hcon, bterr(status)); + l2cap_conn_del(hcon, bt_err(status)); return 0; } @@ -1822,7 +1822,7 @@ static int l2cap_disconn_ind(struct hci_conn *hcon, __u8 reason) if (hcon->type != ACL_LINK) return 0; - l2cap_conn_del(hcon, bterr(reason)); + l2cap_conn_del(hcon, bt_err(reason)); return 0; } @@ -1958,7 +1958,7 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, __u16 } /* Allocate skb for the complete frame (with header) */ - if (!(conn->rx_skb = bluez_skb_alloc(len, GFP_ATOMIC))) + if (!(conn->rx_skb = bt_skb_alloc(len, GFP_ATOMIC))) goto drop; memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len); @@ -1996,7 +1996,7 @@ drop: } /* ----- Proc fs support ------ */ -static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list) +static int l2cap_sock_dump(char *buf, struct bt_sock_list *list) { struct l2cap_pinfo *pi; struct sock *sk; @@ -2007,7 +2007,7 @@ static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list) for (sk = list->head; sk; sk = sk->next) { pi = l2cap_pi(sk); ptr += sprintf(ptr, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d 0x%x\n", - batostr(&bluez_sk(sk)->src), batostr(&bluez_sk(sk)->dst), + batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), sk->state, pi->psm, pi->scid, pi->dcid, pi->imtu, pi->omtu, pi->link_mode); } @@ -2051,8 +2051,8 @@ static struct proto_ops l2cap_sock_ops = { .accept = l2cap_sock_accept, .getname = l2cap_sock_getname, .sendmsg = l2cap_sock_sendmsg, - .recvmsg = bluez_sock_recvmsg, - .poll = bluez_sock_poll, + .recvmsg = bt_sock_recvmsg, + .poll = bt_sock_poll, .mmap = sock_no_mmap, .socketpair = sock_no_socketpair, .ioctl = sock_no_ioctl, @@ -2081,7 +2081,7 @@ int __init l2cap_init(void) { int err; - if ((err = bluez_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops))) { + if ((err = bt_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops))) { BT_ERR("Can't register L2CAP socket"); return err; } @@ -2093,7 +2093,7 @@ int __init l2cap_init(void) create_proc_read_entry("bluetooth/l2cap", 0, 0, l2cap_read_proc, NULL); - BT_INFO("BlueZ L2CAP ver %s Copyright (C) 2000,2001 Qualcomm Inc", VERSION); + BT_INFO("Bluetooth L2CAP ver %s Copyright (C) 2000,2001 Qualcomm Inc", VERSION); BT_INFO("Written 2000,2001 by Maxim Krasnyansky "); return 0; } @@ -2103,7 +2103,7 @@ void l2cap_cleanup(void) remove_proc_entry("bluetooth/l2cap", NULL); /* Unregister socket and protocol */ - if (bluez_sock_unregister(BTPROTO_L2CAP)) + if (bt_sock_unregister(BTPROTO_L2CAP)) BT_ERR("Can't unregister L2CAP socket"); if (hci_unregister_proto(&l2cap_hci_proto)) @@ -2114,5 +2114,5 @@ module_init(l2cap_init); module_exit(l2cap_cleanup); MODULE_AUTHOR("Maxim Krasnyansky "); -MODULE_DESCRIPTION("BlueZ L2CAP ver " VERSION); +MODULE_DESCRIPTION("Bluetooth L2CAP ver " VERSION); MODULE_LICENSE("GPL"); diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c index 3fbcbd646d9f..44c022292f66 100644 --- a/net/bluetooth/lib.c +++ b/net/bluetooth/lib.c @@ -23,7 +23,7 @@ */ /* - * BlueZ kernel library. + * Bluetooth kernel library. * * $Id: lib.c,v 1.1 2002/03/08 21:06:59 maxk Exp $ */ @@ -35,7 +35,7 @@ #include -void bluez_dump(char *pref, __u8 *buf, int count) +void bt_dump(char *pref, __u8 *buf, int count) { char *ptr; char line[100]; @@ -83,7 +83,7 @@ char *batostr(bdaddr_t *ba) } /* Bluetooth error codes to Unix errno mapping */ -int bterr(__u16 code) +int bt_err(__u16 code) { switch (code) { case 0: @@ -171,5 +171,5 @@ int bterr(__u16 code) default: return ENOSYS; - }; + } } diff --git a/net/bluetooth/rfcomm/Config.help b/net/bluetooth/rfcomm/Config.help index 6d5ee6a77f04..27e9d505bd0e 100644 --- a/net/bluetooth/rfcomm/Config.help +++ b/net/bluetooth/rfcomm/Config.help @@ -1,5 +1,5 @@ RFCOMM protocol support -CONFIG_BLUEZ_RFCOMM +CONFIG_BT_RFCOMM RFCOMM provides connection oriented stream transport. RFCOMM support is required for Dialup Networking, OBEX and other Bluetooth applications. @@ -8,5 +8,5 @@ CONFIG_BLUEZ_RFCOMM compile it as module (rfcomm.o). RFCOMM TTY emulation support -CONFIG_BLUEZ_RFCOMM_TTY +CONFIG_BT_RFCOMM_TTY This option enables TTY emulation support for RFCOMM channels. diff --git a/net/bluetooth/rfcomm/Config.in b/net/bluetooth/rfcomm/Config.in index e3ad1358f873..9dc852225dd5 100644 --- a/net/bluetooth/rfcomm/Config.in +++ b/net/bluetooth/rfcomm/Config.in @@ -1,7 +1,7 @@ -dep_tristate 'RFCOMM protocol support' CONFIG_BLUEZ_RFCOMM $CONFIG_BLUEZ_L2CAP +dep_tristate 'RFCOMM protocol support' CONFIG_BT_RFCOMM $CONFIG_BT_L2CAP -if [ "$CONFIG_BLUEZ_RFCOMM" != "n" ]; then - bool ' RFCOMM TTY support' CONFIG_BLUEZ_RFCOMM_TTY +if [ "$CONFIG_BT_RFCOMM" != "n" ]; then + bool ' RFCOMM TTY support' CONFIG_BT_RFCOMM_TTY fi diff --git a/net/bluetooth/rfcomm/Makefile b/net/bluetooth/rfcomm/Makefile index 94fa0f6e1bba..9cae42fee3d6 100644 --- a/net/bluetooth/rfcomm/Makefile +++ b/net/bluetooth/rfcomm/Makefile @@ -2,10 +2,10 @@ # Makefile for the Linux Bluetooth RFCOMM layer. # -obj-$(CONFIG_BLUEZ_RFCOMM) += rfcomm.o +obj-$(CONFIG_BT_RFCOMM) += rfcomm.o rfcomm-y := core.o sock.o crc.o -rfcomm-$(CONFIG_BLUEZ_RFCOMM_TTY) += tty.o +rfcomm-$(CONFIG_BT_RFCOMM_TTY) += tty.o rfcomm-objs := $(rfcomm-y) include $(TOPDIR)/Rules.make diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 230adb8abc89..2d0e0bfc8271 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -26,7 +26,7 @@ */ /* - * RFCOMM core. + * Bluetooth RFCOMM core. * * $Id: core.c,v 1.42 2002/10/01 23:26:25 maxk Exp $ */ @@ -53,7 +53,7 @@ #define VERSION "0.3" -#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG +#ifndef CONFIG_BT_RFCOMM_DEBUG #undef BT_DBG #define BT_DBG(D...) #endif @@ -489,10 +489,10 @@ struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst) { struct rfcomm_session *s; struct list_head *p, *n; - struct bluez_sock *sk; + struct bt_sock *sk; list_for_each_safe(p, n, &session_list) { s = list_entry(p, struct rfcomm_session, list); - sk = bluez_sk(s->sock->sk); + sk = bt_sk(s->sock->sk); if ((!bacmp(src, BDADDR_ANY) || !bacmp(&sk->src, src)) && !bacmp(&sk->dst, dst)) @@ -577,9 +577,9 @@ void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *d { struct sock *sk = s->sock->sk; if (src) - bacpy(src, &bluez_sk(sk)->src); + bacpy(src, &bt_sk(sk)->src); if (dst) - bacpy(dst, &bluez_sk(sk)->dst); + bacpy(dst, &bt_sk(sk)->dst); } /* ---- RFCOMM frame sending ---- */ @@ -1509,7 +1509,7 @@ static inline void rfcomm_accept_connection(struct rfcomm_session *s) /* Fast check for a new connection. * Avoids unnesesary socket allocations. */ - if (list_empty(&bluez_sk(sock->sk)->accept_q)) + if (list_empty(&bt_sk(sock->sk)->accept_q)) return; BT_DBG("session %p", s); @@ -1727,7 +1727,7 @@ static int rfcomm_dlc_dump(char *buf) d = list_entry(pp, struct rfcomm_dlc, list); ptr += sprintf(ptr, "dlc %s %s %ld %d %d %d %d\n", - batostr(&bluez_sk(sk)->src), batostr(&bluez_sk(sk)->dst), + batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), d->state, d->dlci, d->mtu, d->rx_credits, d->tx_credits); } } @@ -1771,13 +1771,13 @@ int __init rfcomm_init(void) rfcomm_init_sockets(); -#ifdef CONFIG_BLUEZ_RFCOMM_TTY +#ifdef CONFIG_BT_RFCOMM_TTY rfcomm_init_ttys(); #endif create_proc_read_entry("bluetooth/rfcomm", 0, 0, rfcomm_read_proc, NULL); - BT_INFO("BlueZ RFCOMM ver %s", VERSION); + BT_INFO("Bluetooth RFCOMM ver %s", VERSION); BT_INFO("Copyright (C) 2002 Maxim Krasnyansky "); BT_INFO("Copyright (C) 2002 Marcel Holtmann "); return 0; @@ -1796,7 +1796,7 @@ void rfcomm_cleanup(void) remove_proc_entry("bluetooth/rfcomm", NULL); -#ifdef CONFIG_BLUEZ_RFCOMM_TTY +#ifdef CONFIG_BT_RFCOMM_TTY rfcomm_cleanup_ttys(); #endif @@ -1808,5 +1808,5 @@ module_init(rfcomm_init); module_exit(rfcomm_cleanup); MODULE_AUTHOR("Maxim Krasnyansky , Marcel Holtmann "); -MODULE_DESCRIPTION("BlueZ RFCOMM ver " VERSION); +MODULE_DESCRIPTION("Bluetooth RFCOMM ver " VERSION); MODULE_LICENSE("GPL"); diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 97277e1a60d9..c7bf6635d467 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -52,14 +52,14 @@ #include #include -#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG +#ifndef CONFIG_BT_RFCOMM_DEBUG #undef BT_DBG #define BT_DBG(D...) #endif static struct proto_ops rfcomm_sock_ops; -static struct bluez_sock_list rfcomm_sk_list = { +static struct bt_sock_list rfcomm_sk_list = { .lock = RW_LOCK_UNLOCKED }; @@ -98,10 +98,10 @@ static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err) sk->err = err; sk->state = d->state; - parent = bluez_sk(sk)->parent; + parent = bt_sk(sk)->parent; if (!parent) { if (d->state == BT_CONNECTED) - rfcomm_session_getaddr(d->session, &bluez_sk(sk)->src, NULL); + rfcomm_session_getaddr(d->session, &bt_sk(sk)->src, NULL); sk->state_change(sk); } else parent->data_ready(parent, 0); @@ -122,7 +122,7 @@ static struct sock *__rfcomm_get_sock_by_addr(int channel, bdaddr_t *src) for (sk = rfcomm_sk_list.head; sk; sk = sk->next) { if (rfcomm_pi(sk)->channel == channel && - !bacmp(&bluez_sk(sk)->src, src)) + !bacmp(&bt_sk(sk)->src, src)) break; } @@ -142,11 +142,11 @@ static struct sock *__rfcomm_get_sock_by_channel(int state, __u16 channel, bdadd if (rfcomm_pi(sk)->channel == channel) { /* Exact match. */ - if (!bacmp(&bluez_sk(sk)->src, src)) + if (!bacmp(&bt_sk(sk)->src, src)) break; /* Closest match */ - if (!bacmp(&bluez_sk(sk)->src, BDADDR_ANY)) + if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) sk1 = sk; } } @@ -197,7 +197,7 @@ static void rfcomm_sock_cleanup_listen(struct sock *parent) BT_DBG("parent %p", parent); /* Close not yet accepted dlcs */ - while ((sk = bluez_accept_dequeue(parent, NULL))) + while ((sk = bt_accept_dequeue(parent, NULL))) rfcomm_sock_close(sk); parent->state = BT_CLOSED; @@ -215,7 +215,7 @@ static void rfcomm_sock_kill(struct sock *sk) BT_DBG("sk %p state %d refcnt %d", sk, sk->state, atomic_read(&sk->refcnt)); /* Kill poor orphan */ - bluez_sock_unlink(&rfcomm_sk_list, sk); + bt_sock_unlink(&rfcomm_sk_list, sk); sk->dead = 1; sock_put(sk); } @@ -265,7 +265,7 @@ static struct sock *rfcomm_sock_alloc(struct socket *sock, int proto, int prio) struct rfcomm_dlc *d; struct sock *sk; - sk = bluez_sock_alloc(sock, BTPROTO_RFCOMM, sizeof(struct rfcomm_pinfo), prio); + sk = bt_sock_alloc(sock, BTPROTO_RFCOMM, sizeof(struct rfcomm_pinfo), prio); if (!sk) return NULL; @@ -290,7 +290,7 @@ static struct sock *rfcomm_sock_alloc(struct socket *sock, int proto, int prio) sk->protocol = proto; sk->state = BT_OPEN; - bluez_sock_link(&rfcomm_sk_list, sk); + bt_sock_link(&rfcomm_sk_list, sk); BT_DBG("sk %p", sk); @@ -342,7 +342,7 @@ static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr err = -EADDRINUSE; } else { /* Save source address */ - bacpy(&bluez_sk(sk)->src, &sa->rc_bdaddr); + bacpy(&bt_sk(sk)->src, &sa->rc_bdaddr); rfcomm_pi(sk)->channel = sa->rc_channel; sk->state = BT_BOUND; } @@ -375,12 +375,12 @@ static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int a lock_sock(sk); sk->state = BT_CONNECT; - bacpy(&bluez_sk(sk)->dst, &sa->rc_bdaddr); + bacpy(&bt_sk(sk)->dst, &sa->rc_bdaddr); rfcomm_pi(sk)->channel = sa->rc_channel; - err = rfcomm_dlc_open(d, &bluez_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel); + err = rfcomm_dlc_open(d, &bt_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel); if (!err) - err = bluez_sock_w4_connect(sk, flags); + err = bt_sock_w4_connect(sk, flags); release_sock(sk); return err; @@ -429,7 +429,7 @@ int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int flags) /* Wait for an incoming connection. (wake-one). */ add_wait_queue_exclusive(sk->sleep, &wait); - while (!(nsk = bluez_accept_dequeue(sk, newsock))) { + while (!(nsk = bt_accept_dequeue(sk, newsock))) { set_current_state(TASK_INTERRUPTIBLE); if (!timeo) { err = -EAGAIN; @@ -475,9 +475,9 @@ static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int * sa->rc_family = AF_BLUETOOTH; sa->rc_channel = rfcomm_pi(sk)->channel; if (peer) - bacpy(&sa->rc_bdaddr, &bluez_sk(sk)->dst); + bacpy(&sa->rc_bdaddr, &bt_sk(sk)->dst); else - bacpy(&sa->rc_bdaddr, &bluez_sk(sk)->src); + bacpy(&sa->rc_bdaddr, &bt_sk(sk)->src); *len = sizeof(struct sockaddr_rc); return 0; @@ -707,7 +707,7 @@ static int rfcomm_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned lon lock_sock(sk); -#ifdef CONFIG_BLUEZ_RFCOMM_TTY +#ifdef CONFIG_BT_RFCOMM_TTY err = rfcomm_dev_ioctl(sk, cmd, arg); #else err = -EOPNOTSUPP; @@ -762,12 +762,12 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc * goto done; rfcomm_sock_init(sk, parent); - bacpy(&bluez_sk(sk)->src, &src); - bacpy(&bluez_sk(sk)->dst, &dst); + bacpy(&bt_sk(sk)->src, &src); + bacpy(&bt_sk(sk)->dst, &dst); rfcomm_pi(sk)->channel = channel; sk->state = BT_CONFIG; - bluez_accept_enqueue(parent, sk); + bt_accept_enqueue(parent, sk); /* Accept connection and return socket DLC */ *d = rfcomm_pi(sk)->dlc; @@ -781,7 +781,7 @@ done: /* ---- Proc fs support ---- */ int rfcomm_sock_dump(char *buf) { - struct bluez_sock_list *list = &rfcomm_sk_list; + struct bt_sock_list *list = &rfcomm_sk_list; struct rfcomm_pinfo *pi; struct sock *sk; char *ptr = buf; @@ -791,7 +791,7 @@ int rfcomm_sock_dump(char *buf) for (sk = list->head; sk; sk = sk->next) { pi = rfcomm_pi(sk); ptr += sprintf(ptr, "sk %s %s %d %d\n", - batostr(&bluez_sk(sk)->src), batostr(&bluez_sk(sk)->dst), + batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), sk->state, rfcomm_pi(sk)->channel); } @@ -814,7 +814,7 @@ static struct proto_ops rfcomm_sock_ops = { .setsockopt = rfcomm_sock_setsockopt, .getsockopt = rfcomm_sock_getsockopt, .ioctl = rfcomm_sock_ioctl, - .poll = bluez_sock_poll, + .poll = bt_sock_poll, .socketpair = sock_no_socketpair, .mmap = sock_no_mmap }; @@ -828,7 +828,7 @@ int rfcomm_init_sockets(void) { int err; - if ((err = bluez_sock_register(BTPROTO_RFCOMM, &rfcomm_sock_family_ops))) { + if ((err = bt_sock_register(BTPROTO_RFCOMM, &rfcomm_sock_family_ops))) { BT_ERR("Can't register RFCOMM socket layer"); return err; } @@ -841,6 +841,6 @@ void rfcomm_cleanup_sockets(void) int err; /* Unregister socket, protocol and notifier */ - if ((err = bluez_sock_unregister(BTPROTO_RFCOMM))) + if ((err = bt_sock_unregister(BTPROTO_RFCOMM))) BT_ERR("Can't unregister RFCOMM socket layer %d", err); } diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index d46caaa5454a..10614ef8120b 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -40,7 +40,7 @@ #include #include -#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG +#ifndef CONFIG_BT_RFCOMM_DEBUG #undef BT_DBG #define BT_DBG(D...) #endif diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 57e44e41f58d..e9b04be730ca 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -23,7 +23,7 @@ */ /* - * BlueZ SCO sockets. + * Bluetooth SCO sockets. * * $Id: sco.c,v 1.3 2002/04/17 17:37:16 maxk Exp $ */ @@ -56,14 +56,14 @@ #include #include -#ifndef SCO_DEBUG +#ifndef CONFIG_BT_SCO_DEBUG #undef BT_DBG #define BT_DBG( A... ) #endif static struct proto_ops sco_sock_ops; -static struct bluez_sock_list sco_sk_list = { +static struct bt_sock_list sco_sk_list = { .lock = RW_LOCK_UNLOCKED }; @@ -200,8 +200,8 @@ static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct so int sco_connect(struct sock *sk) { - bdaddr_t *src = &bluez_sk(sk)->src; - bdaddr_t *dst = &bluez_sk(sk)->dst; + bdaddr_t *src = &bt_sk(sk)->src; + bdaddr_t *dst = &bt_sk(sk)->dst; struct sco_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; @@ -259,7 +259,7 @@ static inline int sco_send_frame(struct sock *sk, struct msghdr *msg, int len) BT_DBG("sk %p len %d", sk, len); count = MIN(conn->mtu, len); - if (!(skb = bluez_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err))) + if (!(skb = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err))) return err; if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) { @@ -303,7 +303,7 @@ static struct sock *__sco_get_sock_by_addr(bdaddr_t *ba) struct sock *sk; for (sk = sco_sk_list.head; sk; sk = sk->next) { - if (!bacmp(&bluez_sk(sk)->src, ba)) + if (!bacmp(&bt_sk(sk)->src, ba)) break; } @@ -324,11 +324,11 @@ static struct sock *sco_get_sock_listen(bdaddr_t *src) continue; /* Exact match. */ - if (!bacmp(&bluez_sk(sk)->src, src)) + if (!bacmp(&bt_sk(sk)->src, src)) break; /* Closest match */ - if (!bacmp(&bluez_sk(sk)->src, BDADDR_ANY)) + if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) sk1 = sk; } @@ -357,7 +357,7 @@ static void sco_sock_cleanup_listen(struct sock *parent) BT_DBG("parent %p", parent); /* Close not yet accepted channels */ - while ((sk = bluez_accept_dequeue(parent, NULL))) + while ((sk = bt_accept_dequeue(parent, NULL))) sco_sock_close(sk); parent->state = BT_CLOSED; @@ -375,7 +375,7 @@ static void sco_sock_kill(struct sock *sk) BT_DBG("sk %p state %d", sk, sk->state); /* Kill poor orphan */ - bluez_sock_unlink(&sco_sk_list, sk); + bt_sock_unlink(&sco_sk_list, sk); sk->dead = 1; sock_put(sk); } @@ -429,7 +429,7 @@ static struct sock *sco_sock_alloc(struct socket *sock, int proto, int prio) { struct sock *sk; - sk = bluez_sock_alloc(sock, proto, sizeof(struct sco_pinfo), prio); + sk = bt_sock_alloc(sock, proto, sizeof(struct sco_pinfo), prio); if (!sk) return NULL; @@ -439,7 +439,7 @@ static struct sock *sco_sock_alloc(struct socket *sock, int proto, int prio) sco_sock_init_timer(sk); - bluez_sock_link(&sco_sk_list, sk); + bt_sock_link(&sco_sk_list, sk); MOD_INC_USE_COUNT; return sk; @@ -490,7 +490,7 @@ static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le err = -EADDRINUSE; } else { /* Save source address */ - bacpy(&bluez_sk(sk)->src, &sa->sco_bdaddr); + bacpy(&bt_sk(sk)->src, &sa->sco_bdaddr); sk->state = BT_BOUND; } @@ -522,12 +522,12 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen lock_sock(sk); /* Set destination address and psm */ - bacpy(&bluez_sk(sk)->dst, &sa->sco_bdaddr); + bacpy(&bt_sk(sk)->dst, &sa->sco_bdaddr); if ((err = sco_connect(sk))) goto done; - err = bluez_sock_w4_connect(sk, flags); + err = bt_sock_w4_connect(sk, flags); done: release_sock(sk); @@ -577,7 +577,7 @@ int sco_sock_accept(struct socket *sock, struct socket *newsock, int flags) /* Wait for an incoming connection. (wake-one). */ add_wait_queue_exclusive(sk->sleep, &wait); - while (!(ch = bluez_accept_dequeue(sk, newsock))) { + while (!(ch = bt_accept_dequeue(sk, newsock))) { set_current_state(TASK_INTERRUPTIBLE); if (!timeo) { err = -EAGAIN; @@ -624,9 +624,9 @@ static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len *len = sizeof(struct sockaddr_sco); if (peer) - bacpy(&sa->sco_bdaddr, &bluez_sk(sk)->dst); + bacpy(&sa->sco_bdaddr, &bt_sk(sk)->dst); else - bacpy(&sa->sco_bdaddr, &bluez_sk(sk)->src); + bacpy(&sa->sco_bdaddr, &bt_sk(sk)->src); return 0; } @@ -751,7 +751,7 @@ static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock * conn->sk = sk; if (parent) - bluez_accept_enqueue(parent, sk); + bt_accept_enqueue(parent, sk); } /* Delete channel. @@ -808,8 +808,8 @@ static void sco_conn_ready(struct sco_conn *conn) sco_sock_init(sk, parent); - bacpy(&bluez_sk(sk)->src, conn->src); - bacpy(&bluez_sk(sk)->dst, conn->dst); + bacpy(&bt_sk(sk)->src, conn->src); + bacpy(&bt_sk(sk)->dst, conn->dst); hci_conn_hold(conn->hcon); __sco_chan_add(conn, sk, parent); @@ -849,7 +849,7 @@ int sco_connect_cfm(struct hci_conn *hcon, __u8 status) if (conn) sco_conn_ready(conn); } else - sco_conn_del(hcon, bterr(status)); + sco_conn_del(hcon, bt_err(status)); return 0; } @@ -861,7 +861,7 @@ int sco_disconn_ind(struct hci_conn *hcon, __u8 reason) if (hcon->type != SCO_LINK) return 0; - sco_conn_del(hcon, bterr(reason)); + sco_conn_del(hcon, bt_err(reason)); return 0; } @@ -885,7 +885,7 @@ drop: } /* ----- Proc fs support ------ */ -static int sco_sock_dump(char *buf, struct bluez_sock_list *list) +static int sco_sock_dump(char *buf, struct bt_sock_list *list) { struct sco_pinfo *pi; struct sock *sk; @@ -896,7 +896,7 @@ static int sco_sock_dump(char *buf, struct bluez_sock_list *list) for (sk = list->head; sk; sk = sk->next) { pi = sco_pi(sk); ptr += sprintf(ptr, "%s %s %d\n", - batostr(&bluez_sk(sk)->src), batostr(&bluez_sk(sk)->dst), + batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), sk->state); } @@ -940,8 +940,8 @@ static struct proto_ops sco_sock_ops = { .accept = sco_sock_accept, .getname = sco_sock_getname, .sendmsg = sco_sock_sendmsg, - .recvmsg = bluez_sock_recvmsg, - .poll = bluez_sock_poll, + .recvmsg = bt_sock_recvmsg, + .poll = bt_sock_poll, .ioctl = sock_no_ioctl, .mmap = sock_no_mmap, .socketpair = sock_no_socketpair, @@ -968,7 +968,7 @@ int __init sco_init(void) { int err; - if ((err = bluez_sock_register(BTPROTO_SCO, &sco_sock_family_ops))) { + if ((err = bt_sock_register(BTPROTO_SCO, &sco_sock_family_ops))) { BT_ERR("Can't register SCO socket layer"); return err; } @@ -980,7 +980,7 @@ int __init sco_init(void) create_proc_read_entry("bluetooth/sco", 0, 0, sco_read_proc, NULL); - BT_INFO("BlueZ SCO ver %s Copyright (C) 2000,2001 Qualcomm Inc", VERSION); + BT_INFO("Bluetooth SCO ver %s Copyright (C) 2000,2001 Qualcomm Inc", VERSION); BT_INFO("Written 2000,2001 by Maxim Krasnyansky "); return 0; } @@ -992,7 +992,7 @@ void sco_cleanup(void) remove_proc_entry("bluetooth/sco", NULL); /* Unregister socket, protocol and notifier */ - if ((err = bluez_sock_unregister(BTPROTO_SCO))) + if ((err = bt_sock_unregister(BTPROTO_SCO))) BT_ERR("Can't unregister SCO socket layer %d", err); if ((err = hci_unregister_proto(&sco_hci_proto))) @@ -1003,5 +1003,5 @@ module_init(sco_init); module_exit(sco_cleanup); MODULE_AUTHOR("Maxim Krasnyansky "); -MODULE_DESCRIPTION("BlueZ SCO ver " VERSION); +MODULE_DESCRIPTION("Bluetooth SCO ver " VERSION); MODULE_LICENSE("GPL"); diff --git a/net/bluetooth/syms.c b/net/bluetooth/syms.c index 6ec6c7123e60..a1d3c0e6d384 100644 --- a/net/bluetooth/syms.c +++ b/net/bluetooth/syms.c @@ -23,7 +23,7 @@ */ /* - * BlueZ symbols. + * Bluetooth symbols. * * $Id: syms.c,v 1.1 2002/03/08 21:06:59 maxk Exp $ */ @@ -59,20 +59,20 @@ EXPORT_SYMBOL(hci_send_sco); EXPORT_SYMBOL(hci_send_raw); EXPORT_SYMBOL(hci_si_event); -/* BlueZ lib */ -EXPORT_SYMBOL(bluez_dump); +/* Bluetooth lib */ +EXPORT_SYMBOL(bt_dump); EXPORT_SYMBOL(baswap); EXPORT_SYMBOL(batostr); -EXPORT_SYMBOL(bterr); +EXPORT_SYMBOL(bt_err); -/* BlueZ sockets */ -EXPORT_SYMBOL(bluez_sock_register); -EXPORT_SYMBOL(bluez_sock_unregister); -EXPORT_SYMBOL(bluez_sock_alloc); -EXPORT_SYMBOL(bluez_sock_link); -EXPORT_SYMBOL(bluez_sock_unlink); -EXPORT_SYMBOL(bluez_sock_recvmsg); -EXPORT_SYMBOL(bluez_sock_poll); -EXPORT_SYMBOL(bluez_accept_enqueue); -EXPORT_SYMBOL(bluez_accept_dequeue); -EXPORT_SYMBOL(bluez_sock_w4_connect); +/* Bluetooth sockets */ +EXPORT_SYMBOL(bt_sock_register); +EXPORT_SYMBOL(bt_sock_unregister); +EXPORT_SYMBOL(bt_sock_alloc); +EXPORT_SYMBOL(bt_sock_link); +EXPORT_SYMBOL(bt_sock_unlink); +EXPORT_SYMBOL(bt_sock_recvmsg); +EXPORT_SYMBOL(bt_sock_poll); +EXPORT_SYMBOL(bt_accept_enqueue); +EXPORT_SYMBOL(bt_accept_dequeue); +EXPORT_SYMBOL(bt_sock_w4_connect); -- cgit v1.2.3 From 63d902f71914cbbbedc5745bc48a1c23b4e5e2ba Mon Sep 17 00:00:00 2001 From: Maksim Krasnyanskiy Date: Sun, 13 Oct 2002 18:17:53 -0700 Subject: Get rid of the MIN() thing in Bluetooth code and use min_t() instead. --- drivers/bluetooth/hci_h4.c | 2 +- drivers/bluetooth/hci_usb.c | 2 +- drivers/bluetooth/hci_vhci.c | 2 +- include/net/bluetooth/bluetooth.h | 4 ---- net/bluetooth/hci_sock.c | 4 ++-- net/bluetooth/l2cap.c | 14 +++++++------- net/bluetooth/sco.c | 6 +++--- 7 files changed, 15 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index b1ba694cf524..521eb19442be 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -160,7 +160,7 @@ static int h4_recv(struct hci_uart *hu, void *data, int count) ptr = data; while (count) { if (h4->rx_count) { - len = MIN(h4->rx_count, count); + len = min_t(unsigned int, h4->rx_count, count); memcpy(skb_put(h4->rx_skb, len), ptr, len); h4->rx_count -= len; count -= len; ptr += len; diff --git a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c index 65248e1df44f..027b4463f3c9 100644 --- a/drivers/bluetooth/hci_usb.c +++ b/drivers/bluetooth/hci_usb.c @@ -639,7 +639,7 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) /* Find endpoints that we need */ - ifn = MIN(udev->actconfig->bNumInterfaces, HCI_MAX_IFACE_NUM); + ifn = min_t(unsigned int, udev->actconfig->bNumInterfaces, HCI_MAX_IFACE_NUM); for (i = 0; i < ifn; i++) { iface = &udev->actconfig->interface[i]; for (a = 0; a < iface->num_altsetting; a++) { diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index aab1aca475d7..a70eef0fd4d9 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -172,7 +172,7 @@ static inline ssize_t hci_vhci_put_user(struct hci_vhci_struct *hci_vhci, int len = count, total = 0; char *ptr = buf; - len = MIN(skb->len, len); + len = min_t(unsigned int, skb->len, len); if (copy_to_user(ptr, skb->data, len)) return -EFAULT; total += len; diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index a7202d48b57c..d8e54c8f4daf 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -42,10 +42,6 @@ /* Reserv for core and drivers use */ #define BT_SKB_RESERVE 8 -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - #define BTPROTO_L2CAP 0 #define BTPROTO_HCI 1 #define BTPROTO_SCO 2 diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index e9379c7f2014..0398bb045b4c 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -454,7 +454,7 @@ int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optva break; case HCI_FILTER: - len = MIN(len, sizeof(uf)); + len = min_t(unsigned int, len, sizeof(uf)); if (copy_from_user(&uf, optval, len)) { err = -EFAULT; break; @@ -525,7 +525,7 @@ int hci_sock_getsockopt(struct socket *sock, int level, int optname, char *optva uf.event_mask[1] = *((u32 *) f->event_mask + 1); } - len = MIN(len, sizeof(uf)); + len = min_t(unsigned int, len, sizeof(uf)); if (copy_to_user(optval, &uf, len)) return -EFAULT; break; diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index cb47e43234e9..28f06d39daab 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -682,7 +682,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch switch (optname) { case L2CAP_OPTIONS: - len = MIN(sizeof(opts), optlen); + len = min_t(unsigned int, sizeof(opts), optlen); if (copy_from_user((char *)&opts, optval, len)) { err = -EFAULT; break; @@ -727,7 +727,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch opts.omtu = l2cap_pi(sk)->omtu; opts.flush_to = l2cap_pi(sk)->flush_to; - len = MIN(len, sizeof(opts)); + len = min_t(unsigned int, len, sizeof(opts)); if (copy_to_user(optval, (char *)&opts, len)) err = -EFAULT; @@ -746,7 +746,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch cinfo.hci_handle = l2cap_pi(sk)->conn->hcon->handle; - len = MIN(len, sizeof(cinfo)); + len = min_t(unsigned int, len, sizeof(cinfo)); if (copy_to_user(optval, (char *)&cinfo, len)) err = -EFAULT; @@ -1017,7 +1017,7 @@ static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len) else hlen = L2CAP_HDR_SIZE; - count = MIN(conn->mtu - hlen, len); + count = min_t(unsigned int, (conn->mtu - hlen), len); skb = bt_skb_send_alloc(sk, hlen + count, msg->msg_flags & MSG_DONTWAIT, &err); @@ -1043,7 +1043,7 @@ static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len) /* Continuation fragments (no L2CAP header) */ frag = &skb_shinfo(skb)->frag_list; while (len) { - count = MIN(conn->mtu, len); + count = min_t(unsigned int, conn->mtu, len); *frag = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err); if (!*frag) @@ -1103,7 +1103,7 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, BT_DBG("conn %p, code 0x%2.2x, ident 0x%2.2x, len %d", conn, code, ident, dlen); len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen; - count = MIN(conn->mtu, len); + count = min_t(unsigned int, conn->mtu, len); skb = bt_skb_alloc(count, GFP_ATOMIC); if (!skb) @@ -1129,7 +1129,7 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, /* Continuation fragments (no L2CAP header) */ frag = &skb_shinfo(skb)->frag_list; while (len) { - count = MIN(conn->mtu, len); + count = min_t(unsigned int, conn->mtu, len); *frag = bt_skb_alloc(count, GFP_ATOMIC); if (!*frag) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index e9b04be730ca..defa54f0afc3 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -258,7 +258,7 @@ static inline int sco_send_frame(struct sock *sk, struct msghdr *msg, int len) BT_DBG("sk %p len %d", sk, len); - count = MIN(conn->mtu, len); + count = min_t(unsigned int, conn->mtu, len); if (!(skb = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err))) return err; @@ -699,7 +699,7 @@ int sco_sock_getsockopt(struct socket *sock, int level, int optname, char *optva BT_INFO("mtu %d", opts.mtu); - len = MIN(len, sizeof(opts)); + len = min_t(unsigned int, len, sizeof(opts)); if (copy_to_user(optval, (char *)&opts, len)) err = -EFAULT; @@ -713,7 +713,7 @@ int sco_sock_getsockopt(struct socket *sock, int level, int optname, char *optva cinfo.hci_handle = sco_pi(sk)->conn->hcon->handle; - len = MIN(len, sizeof(cinfo)); + len = min_t(unsigned int, len, sizeof(cinfo)); if (copy_to_user(optval, (char *)&cinfo, len)) err = -EFAULT; -- cgit v1.2.3 From 4c9eb495b27f5939dc0a9ed4ed30200e91d960e5 Mon Sep 17 00:00:00 2001 From: Maksim Krasnyanskiy Date: Sun, 13 Oct 2002 18:36:42 -0700 Subject: Support for suspend/resume interface for the HCI devices. --- include/net/bluetooth/hci.h | 2 ++ include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_core.c | 16 ++++++++++++++++ net/bluetooth/syms.c | 3 +++ 4 files changed, 23 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index d67d9721e091..a8832055d170 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -39,6 +39,8 @@ #define HCI_DEV_UNREG 2 #define HCI_DEV_UP 3 #define HCI_DEV_DOWN 4 +#define HCI_DEV_SUSPEND 5 +#define HCI_DEV_RESUME 6 /* HCI device types */ #define HCI_VHCI 0 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 1740cde1287d..adf5558f660a 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -302,6 +302,8 @@ struct hci_dev *hci_dev_get(int index); struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst); int hci_register_dev(struct hci_dev *hdev); int hci_unregister_dev(struct hci_dev *hdev); +int hci_suspend_dev(struct hci_dev *hdev); +int hci_resume_dev(struct hci_dev *hdev); int hci_dev_open(__u16 dev); int hci_dev_close(__u16 dev); int hci_dev_reset(__u16 dev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f013113f34ec..1ad3339ed38d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -863,6 +863,22 @@ int hci_unregister_dev(struct hci_dev *hdev) return 0; } +/* Suspend HCI device */ +int hci_suspend_dev(struct hci_dev *hdev) +{ + hci_notify(hdev, HCI_DEV_SUSPEND); + hci_run_hotplug(hdev->name, "suspend"); + return 0; +} + +/* Resume HCI device */ +int hci_resume_dev(struct hci_dev *hdev) +{ + hci_notify(hdev, HCI_DEV_RESUME); + hci_run_hotplug(hdev->name, "resume"); + return 0; +} + /* Receive frame from HCI drivers */ int hci_recv_frame(struct sk_buff *skb) { diff --git a/net/bluetooth/syms.c b/net/bluetooth/syms.c index a1d3c0e6d384..779d82130924 100644 --- a/net/bluetooth/syms.c +++ b/net/bluetooth/syms.c @@ -44,6 +44,9 @@ /* HCI Core */ EXPORT_SYMBOL(hci_register_dev); EXPORT_SYMBOL(hci_unregister_dev); +EXPORT_SYMBOL(hci_suspend_dev); +EXPORT_SYMBOL(hci_resume_dev); + EXPORT_SYMBOL(hci_register_proto); EXPORT_SYMBOL(hci_unregister_proto); -- cgit v1.2.3 From 5793a2d523f65c6e22973c1e78d75c4ee609b09e Mon Sep 17 00:00:00 2001 From: Jeff Dike Date: Mon, 14 Oct 2002 01:53:55 -0400 Subject: This is the merge of the initial 2.4 SMP support. Locking was added where necessary. All processors take timer interrupts, but only CPU 0 calls the timer IRQ. The others just call update_process_times to keep the accounting straight. The timer interrupt is blocked along with the other signals. --- arch/um/drivers/chan_user.c | 2 + arch/um/drivers/harddog_kern.c | 4 +- arch/um/drivers/hostaudio_kern.c | 1 + arch/um/drivers/line.c | 22 +++++- arch/um/drivers/mconsole_kern.c | 28 ++++++- arch/um/drivers/mconsole_user.c | 10 ++- arch/um/drivers/mmapper_kern.c | 3 +- arch/um/drivers/net_kern.c | 23 +++++- arch/um/drivers/port_kern.c | 13 +++- arch/um/drivers/ssl.c | 5 +- arch/um/drivers/stdio_console.c | 9 +++ arch/um/drivers/ubd_kern.c | 147 +++++++++++++++++++++++++---------- arch/um/drivers/ubd_user.c | 4 + arch/um/drivers/xterm.c | 1 + arch/um/include/2_5compat.h | 2 - arch/um/include/irq_user.h | 4 +- arch/um/include/kern_util.h | 10 +-- arch/um/include/mconsole.h | 2 + arch/um/include/sigio.h | 2 + arch/um/include/time_user.h | 6 +- arch/um/kernel/exec_kern.c | 3 +- arch/um/kernel/exitcode.c | 3 + arch/um/kernel/frame.c | 4 + arch/um/kernel/helper.c | 1 + arch/um/kernel/initrd_kern.c | 1 + arch/um/kernel/irq.c | 29 ++++++- arch/um/kernel/irq_user.c | 162 +++++++++++++++++++++++++++------------ arch/um/kernel/mem.c | 45 ++++++++--- arch/um/kernel/mem_user.c | 5 +- arch/um/kernel/process.c | 14 ++-- arch/um/kernel/process_kern.c | 49 ++++++++---- arch/um/kernel/sigio_kern.c | 13 ++++ arch/um/kernel/sigio_user.c | 59 ++++++++++---- arch/um/kernel/signal_user.c | 27 ++++--- arch/um/kernel/smp.c | 157 ++++++++++++++++++------------------- arch/um/kernel/syscall_kern.c | 15 ++-- arch/um/kernel/syscall_user.c | 13 +--- arch/um/kernel/time.c | 44 ++++------- arch/um/kernel/time_kern.c | 31 ++++++-- arch/um/kernel/trap_kern.c | 15 ++-- arch/um/kernel/trap_user.c | 79 ++++++++----------- arch/um/kernel/tty_log.c | 4 +- arch/um/kernel/um_arch.c | 29 ++++--- arch/um/kernel/umid.c | 4 + arch/um/kernel/user_util.c | 1 + arch/um/main.c | 13 +++- arch/um/ptproxy/proxy.c | 6 +- arch/um/sys-i386/bugs.c | 1 + arch/um/sys-i386/ptrace_user.c | 1 + arch/um/sys-ppc/miscthings.c | 3 - arch/um/uml.lds.S | 3 + include/asm-um/cache.h | 3 + include/asm-um/smp.h | 17 +++- include/asm-um/thread_info.h | 4 + 54 files changed, 761 insertions(+), 395 deletions(-) (limited to 'include') diff --git a/arch/um/drivers/chan_user.c b/arch/um/drivers/chan_user.c index c1ac4d8fbb26..79879f30aef5 100644 --- a/arch/um/drivers/chan_user.c +++ b/arch/um/drivers/chan_user.c @@ -155,6 +155,8 @@ static void tracer_winch_handler(int sig) errno); } +/* Called only by the tracing thread during initialization */ + void setup_tracer_winch(void) { int err; diff --git a/arch/um/drivers/harddog_kern.c b/arch/um/drivers/harddog_kern.c index dd1214f116ab..efe03723db54 100644 --- a/arch/um/drivers/harddog_kern.c +++ b/arch/um/drivers/harddog_kern.c @@ -51,8 +51,8 @@ MODULE_LICENSE("GPL"); +/* Locked by the BKL in harddog_open and harddog_release */ static int timer_alive; - static int harddog_in_fd = -1; static int harddog_out_fd = -1; @@ -67,6 +67,7 @@ static int harddog_open(struct inode *inode, struct file *file) int err; char *sock = NULL; + lock_kernel(); if(timer_alive) return -EBUSY; #ifdef CONFIG_HARDDOG_NOWAYOUT @@ -80,6 +81,7 @@ static int harddog_open(struct inode *inode, struct file *file) if(err) return(err); timer_alive = 1; + unlock_kernel(); return 0; } diff --git a/arch/um/drivers/hostaudio_kern.c b/arch/um/drivers/hostaudio_kern.c index 5ae1c44294f6..d5c950b4bb83 100644 --- a/arch/um/drivers/hostaudio_kern.c +++ b/arch/um/drivers/hostaudio_kern.c @@ -15,6 +15,7 @@ #include "init.h" #include "hostaudio.h" +/* Only changed from linux_main at boot time */ char *dsp = HOSTAUDIO_DEV_DSP; char *mixer = HOSTAUDIO_DEV_MIXER; diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index 50e9adfa5e75..a7ff39d3862f 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c @@ -99,19 +99,27 @@ int line_write(struct line *lines, struct tty_struct *tty, const char *buf, i = minor(tty->device) - tty->driver.minor_start; line = &lines[i]; + down(&line->sem); if(line->head != line->tail){ local_irq_save(flags); buffer_data(line, buf, len); err = flush_buffer(line); local_irq_restore(flags); - if(err <= 0) return(len); + if(err <= 0) + goto out; } else { n = write_chan(&line->chan_list, buf, len, line->driver->write_irq); - if(n < 0) return(n); - if(n < len) buffer_data(line, buf + n, len - n); + if(n < 0){ + len = n; + goto out; + } + if(n < len) + buffer_data(line, buf + n, len - n); } + out: + up(&line->sem); return(len); } @@ -249,6 +257,7 @@ void line_close(struct line *lines, struct tty_struct *tty) else n = minor(tty->device) - tty->driver.minor_start; line = &lines[n]; + down(&line->sem); line->count--; /* I don't like this, but I can't think of anything better. What's @@ -261,6 +270,7 @@ void line_close(struct line *lines, struct tty_struct *tty) line->tty = NULL; if(line->count == 0) line_disable(line, -1); + up(&line->sem); } void close_lines(struct line *lines, int nlines) @@ -408,16 +418,18 @@ void winch_interrupt(int irq, void *data, struct pt_regs *unused) reactivate_fd(winch->fd, WINCH_IRQ); } +DECLARE_MUTEX(winch_handler_sem); LIST_HEAD(winch_handlers); void register_winch_irq(int fd, int tty_fd, int pid, void *line) { struct winch *winch; + down(&winch_handler_sem); winch = kmalloc(sizeof(*winch), GFP_KERNEL); if(winch == NULL){ printk("register_winch_irq - kmalloc failed\n"); - return; + goto out; } *winch = ((struct winch) { list : LIST_HEAD_INIT(winch->list), fd : fd, @@ -429,6 +441,8 @@ void register_winch_irq(int fd, int tty_fd, int pid, void *line) SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, "winch", winch) < 0) printk("register_winch_irq - failed to register IRQ\n"); + out: + up(&winch_handler_sem); } static void winch_cleanup(void) diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c index a90786aff133..c2822cb872c2 100644 --- a/arch/um/drivers/mconsole_kern.c +++ b/arch/um/drivers/mconsole_kern.c @@ -40,6 +40,11 @@ static struct notifier_block reboot_notifier = { priority: 0, }; +/* Safe without explicit locking for now. Tasklets provide their own + * locking, and the interrupt handler is safe because it can't interrupt + * itself and it can only happen on CPU 0. + */ + LIST_HEAD(mc_requests); void mc_work_proc(void *unused) @@ -49,12 +54,12 @@ void mc_work_proc(void *unused) int done; do { - save_flags(flags); + local_save_flags(flags); req = list_entry(mc_requests.next, struct mconsole_entry, list); list_del(&req->list); done = list_empty(&mc_requests); - restore_flags(flags); + local_irq_restore(flags); req->request.cmd->handler(&req->request); kfree(req); } while(!done); @@ -152,6 +157,8 @@ void mconsole_stop(struct mc_request *req) mconsole_reply(req, "", 0, 0); } +/* This list is populated by __initcall routines. */ + LIST_HEAD(mconsole_devices); void mconsole_register_dev(struct mc_device *new) @@ -224,7 +231,10 @@ void mconsole_sysrq(struct mc_request *req) } #endif -static char *notify_socket = NULL; +/* Changed by mconsole_setup, which is __setup, and called before SMP is + * active. + */ +static char *notify_socket = NULL; int mconsole_init(void) { @@ -301,6 +311,18 @@ static int create_proc_mconsole(void) return(0); } +static spinlock_t notify_spinlock = SPIN_LOCK_UNLOCKED; + +void lock_notify(void) +{ + spin_lock(¬ify_spinlock); +} + +void unlock_notify(void) +{ + spin_unlock(¬ify_spinlock); +} + __initcall(create_proc_mconsole); #define NOTIFY "=notify:" diff --git a/arch/um/drivers/mconsole_user.c b/arch/um/drivers/mconsole_user.c index d60be6eead37..11b09a96f454 100644 --- a/arch/um/drivers/mconsole_user.c +++ b/arch/um/drivers/mconsole_user.c @@ -30,6 +30,7 @@ static struct mconsole_command commands[] = { { "go", mconsole_go, 1 }, }; +/* Initialized in mconsole_init, which is an initcall */ char mconsole_socket_name[256]; int mconsole_reply_v0(struct mc_request *req, char *reply) @@ -162,16 +163,21 @@ int mconsole_notify(char *sock_name, int type, const void *data, int len) { struct sockaddr_un target; struct mconsole_notify packet; - int n, err; + int n, err = 0; + lock_notify(); if(notify_sock < 0){ notify_sock = socket(PF_UNIX, SOCK_DGRAM, 0); if(notify_sock < 0){ printk("mconsole_notify - socket failed, errno = %d\n", errno); - return(-errno); + err = -errno; } } + unlock_notify(); + + if(err) + return(err); target.sun_family = AF_UNIX; strcpy(target.sun_path, sock_name); diff --git a/arch/um/drivers/mmapper_kern.c b/arch/um/drivers/mmapper_kern.c index d03082be380d..dc2937a0ed2e 100644 --- a/arch/um/drivers/mmapper_kern.c +++ b/arch/um/drivers/mmapper_kern.c @@ -15,13 +15,14 @@ #include #include #include +#include #include #include -#include #include #include "mem_user.h" #include "user_util.h" +/* These are set in mmapper_init, which is called at boot time */ static unsigned long mmapper_size; static unsigned long p_buf = 0; static char *v_buf = NULL; diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c index a83f84c367d4..5b35b8e5e74e 100644 --- a/arch/um/drivers/net_kern.c +++ b/arch/um/drivers/net_kern.c @@ -27,6 +27,7 @@ #include "init.h" #include "irq_user.h" +static spinlock_t opened_lock = SPIN_LOCK_UNLOCKED; LIST_HEAD(opened); static int uml_net_rx(struct net_device *dev) @@ -118,7 +119,9 @@ static int uml_net_open(struct net_device *dev) lp->tl.data = (unsigned long) &lp->user; netif_start_queue(dev); + spin_lock(&opened_lock); list_add(&lp->list, &opened); + spin_unlock(&opened_lock); MOD_INC_USE_COUNT; out: spin_unlock(&lp->lock); @@ -135,8 +138,10 @@ static int uml_net_close(struct net_device *dev) free_irq(dev->irq, dev); if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user); lp->fd = -1; + spin_lock(&opened_lock); list_del(&lp->list); - + spin_unlock(&opened_lock); + MOD_DEC_USE_COUNT; spin_unlock(&lp->lock); return 0; @@ -245,6 +250,7 @@ void uml_net_user_timer_expire(unsigned long _conn) #endif } +static spinlock_t devices_lock = SPIN_LOCK_UNLOCKED; static struct list_head devices = LIST_HEAD_INIT(devices); static int eth_configure(int n, void *init, char *mac, @@ -261,7 +267,10 @@ static int eth_configure(int n, void *init, char *mac, return(1); } + spin_lock(&devices_lock); list_add(&device->list, &devices); + spin_unlock(&devices_lock); + device->index = n; size = transport->private_size + sizeof(struct uml_net_private) + @@ -373,12 +382,16 @@ static struct uml_net *find_device(int n) struct uml_net *device; struct list_head *ele; + spin_lock(&devices_lock); list_for_each(ele, &devices){ device = list_entry(ele, struct uml_net, list); if(device->index == n) - return(device); + goto out; } - return(NULL); + device = NULL; + out: + spin_unlock(&devices_lock); + return(device); } static int eth_parse(char *str, int *index_out, char **str_out) @@ -418,8 +431,12 @@ struct eth_init { int index; }; +/* Filled in at boot time. Will need locking if the transports become + * modular. + */ struct list_head transports = LIST_HEAD_INIT(transports); +/* Filled in during early boot */ struct list_head eth_cmd_line = LIST_HEAD_INIT(eth_cmd_line); static int check_transport(struct transport *transport, char *eth, int n, diff --git a/arch/um/drivers/port_kern.c b/arch/um/drivers/port_kern.c index 0acb925a70f6..b85f8f23bd49 100644 --- a/arch/um/drivers/port_kern.c +++ b/arch/um/drivers/port_kern.c @@ -62,8 +62,6 @@ static void pipe_interrupt(int irq, void *data, struct pt_regs *regs) up(&conn->port->sem); } -struct list_head ports = LIST_HEAD_INIT(ports); - static void port_interrupt(int irq, void *data, struct pt_regs *regs) { struct port_list *port = data; @@ -107,6 +105,9 @@ static void port_interrupt(int irq, void *data, struct pt_regs *regs) reactivate_fd(port->fd, ACCEPT_IRQ); } +DECLARE_MUTEX(ports_sem); +struct list_head ports = LIST_HEAD_INIT(ports); + void *port_data(int port_num) { struct list_head *ele; @@ -114,6 +115,7 @@ void *port_data(int port_num) struct port_dev *dev; int fd; + down(&ports_sem); list_for_each(ele, &ports){ port = list_entry(ele, struct port_list, list); if(port->port == port_num) goto found; @@ -121,7 +123,7 @@ void *port_data(int port_num) port = kmalloc(sizeof(struct port_list), GFP_KERNEL); if(port == NULL){ printk(KERN_ERR "Allocation of port list failed\n"); - return(NULL); + goto out; } fd = port_listen_fd(port_num); @@ -151,18 +153,21 @@ void *port_data(int port_num) dev = kmalloc(sizeof(struct port_dev), GFP_KERNEL); if(dev == NULL){ printk(KERN_ERR "Allocation of port device entry failed\n"); - return(NULL); + goto out; } *dev = ((struct port_dev) { port : port, fd : -1, helper_pid : -1 }); + up(&ports_sem); return(dev); out_free: kfree(port); out_close: os_close_file(fd); + out: + up(&ports_sem); return(NULL); } diff --git a/arch/um/drivers/ssl.c b/arch/um/drivers/ssl.c index 5d52974e3726..fce548deafea 100644 --- a/arch/um/drivers/ssl.c +++ b/arch/um/drivers/ssl.c @@ -58,7 +58,10 @@ static struct line_driver driver = { symlink_to : "tts", }; -static struct line serial_lines[NR_PORTS] = +/* The array is initialized by line_init, which is an initcall. The + * individual elements are protected by individual semaphores. + */ +static struct line serial_lines[NR_PORTS] = { [0 ... NR_PORTS - 1] = LINE_INIT(CONFIG_SSL_CHAN, &driver) }; static struct lines lines = LINES_INIT(NR_PORTS); diff --git a/arch/um/drivers/stdio_console.c b/arch/um/drivers/stdio_console.c index 3ef550107a93..979793f47d96 100644 --- a/arch/um/drivers/stdio_console.c +++ b/arch/um/drivers/stdio_console.c @@ -32,8 +32,14 @@ #define MAX_TTYS (8) +/* Referenced only by tty_driver below - presumably it's locked correctly + * by the tty driver. + */ + static struct tty_driver console_driver; +static int console_refcount = 0; + static struct chan_ops init_console_ops = { init : NULL, open : NULL, @@ -88,6 +94,9 @@ static struct line_driver driver = { static struct lines console_lines = LINES_INIT(MAX_TTYS); +/* The array is initialized by line_init, which is an initcall. The + * individual elements are protected by individual semaphores. + */ struct line vts[MAX_TTYS] = { LINE_INIT(CONFIG_CON_ZERO_CHAN, &driver), [ 1 ... MAX_TTYS - 1 ] = LINE_INIT(CONFIG_CON_CHAN, &driver) }; diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c index 36995c3f84f6..6777b7e97003 100644 --- a/arch/um/drivers/ubd_kern.c +++ b/arch/um/drivers/ubd_kern.c @@ -25,6 +25,7 @@ #include "linux/vmalloc.h" #include "linux/blkpg.h" #include "linux/genhd.h" +#include "linux/spinlock.h" #include "asm/segment.h" #include "asm/uaccess.h" #include "asm/irq.h" @@ -41,7 +42,9 @@ #include "2_5compat.h" #include "os.h" -static spinlock_t ubd_lock; +static spinlock_t ubd_io_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t ubd_lock = SPIN_LOCK_UNLOCKED; + static void (*do_ubd)(void); static int ubd_open(struct inode * inode, struct file * filp); @@ -62,9 +65,12 @@ static struct block_device_operations ubd_blops = { .revalidate = ubd_revalidate, }; +/* Protected by the queue_lock */ static request_queue_t *ubd_queue; +/* Protected by ubd_lock */ static int fake_major = 0; + static struct gendisk *ubd_gendisk[MAX_DEV]; static struct gendisk *fake_gendisk[MAX_DEV]; @@ -74,6 +80,9 @@ static struct gendisk *fake_gendisk[MAX_DEV]; #define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0 }) #endif +/* Not protected - changed only in ubd_setup_common and then only to + * to enable O_SYNC. + */ static struct openflags global_openflags = OPEN_FLAGS; struct cow { @@ -170,7 +179,9 @@ static void make_ide_entries(char *dev_name) char name[64]; if(!fake_ide) return; + if(proc_ide_root == NULL) make_proc_ide(); + dir = proc_mkdir(dev_name, proc_ide); ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir); if(!ent) return; @@ -199,7 +210,7 @@ static int ubd_setup_common(char *str, int *index_out) { struct openflags flags = global_openflags; char *backing_file; - int n; + int n, err; if(index_out) *index_out = -1; n = *str++; @@ -224,12 +235,22 @@ static int ubd_setup_common(char *str, int *index_out) return(1); } - fake_major = major; + err = 1; + spin_lock(&ubd_lock); + if(!fake_major_allowed){ + printk(KERN_ERR "Can't assign a fake major twice\n"); + goto out1; + } + + fake_major = major; fake_major_allowed = 0; printk(KERN_INFO "Setting extra ubd major number to %d\n", major); - return(0); + err = 0; + out1: + spin_unlock(&ubd_lock); + return(err); } if(n < '0'){ @@ -247,9 +268,12 @@ static int ubd_setup_common(char *str, int *index_out) return(1); } + err = 1; + spin_lock(&ubd_lock); + if(ubd_dev[n].file != NULL){ printk(KERN_ERR "ubd_setup : device already configured\n"); - return(1); + goto out2; } if(index_out) *index_out = n; @@ -264,8 +288,10 @@ static int ubd_setup_common(char *str, int *index_out) } if(*str++ != '='){ printk(KERN_ERR "ubd_setup : Expected '='\n"); - return(1); + goto out2; } + + err = 0; backing_file = strchr(str, ','); if(backing_file){ *backing_file = '\0'; @@ -276,7 +302,9 @@ static int ubd_setup_common(char *str, int *index_out) ubd_dev[n].is_dir = 1; ubd_dev[n].cow.file = backing_file; ubd_dev[n].boot_openflags = flags; - return(0); + out2: + spin_unlock(&ubd_lock); + return(err); } static int ubd_setup(char *str) @@ -317,8 +345,12 @@ __uml_help(fakehd, static void do_ubd_request(request_queue_t * q); +/* Only changed by ubd_init, which is an initcall. */ int thread_fd = -1; +/* Changed by ubd_handler, which is serialized because interrupts only + * happen on CPU 0. + */ int intr_count = 0; static void ubd_finish(int error) @@ -326,7 +358,9 @@ static void ubd_finish(int error) int nsect; if(error){ + spin_lock(&ubd_io_lock); end_request(CURRENT, 0); + spin_unlock(&ubd_io_lock); return; } nsect = CURRENT->current_nr_sectors; @@ -335,7 +369,9 @@ static void ubd_finish(int error) CURRENT->errors = 0; CURRENT->nr_sectors -= nsect; CURRENT->current_nr_sectors = 0; + spin_lock(&ubd_io_lock); end_request(CURRENT, 1); + spin_unlock(&ubd_io_lock); } static void ubd_handler(void) @@ -349,9 +385,9 @@ static void ubd_handler(void) if(n != sizeof(req)){ printk(KERN_ERR "Pid %d - spurious interrupt in ubd_handler, " "errno = %d\n", os_getpid(), -n); - spin_lock(&ubd_lock); + spin_lock(&ubd_io_lock); end_request(CURRENT, 0); - spin_unlock(&ubd_lock); + spin_unlock(&ubd_io_lock); return; } @@ -359,11 +395,9 @@ static void ubd_handler(void) (req.length != (CURRENT->current_nr_sectors) << 9)) panic("I/O op mismatch"); - spin_lock(&ubd_lock); ubd_finish(req.error); reactivate_fd(thread_fd, UBD_IRQ); do_ubd_request(ubd_queue); - spin_unlock(&ubd_lock); } static void ubd_intr(int irq, void *dev, struct pt_regs *unused) @@ -371,6 +405,7 @@ static void ubd_intr(int irq, void *dev, struct pt_regs *unused) ubd_handler(); } +/* Only changed by ubd_init, which is an initcall. */ static int io_pid = -1; void kill_io_thread(void) @@ -390,6 +425,7 @@ static int ubd_file_size(struct ubd *dev, __u64 *size_out) return(os_file_size(file, size_out)); } +/* Initialized in an initcall, and unchanged thereafter */ devfs_handle_t ubd_dir_handle; devfs_handle_t ubd_fake_dir_handle; @@ -402,7 +438,7 @@ static int ubd_add(int n) u64 size; if (!dev->file) - return -1; + goto out; disk = alloc_disk(); if (!disk) @@ -443,23 +479,32 @@ static int ubd_add(int n) MAJOR_NR, n << UBD_SHIFT, S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP |S_IWGRP, &ubd_blops, NULL); - add_disk(disk); + if(real == NULL) + goto out; + ubd_dev[n].real = real; + if (fake_major) { fake = devfs_register(ubd_fake_dir_handle, name, DEVFS_FL_REMOVABLE, fake_major, n << UBD_SHIFT, S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, &ubd_blops, NULL); - add_disk(fake_disk); - if(fake == NULL) return(-1); + if(fake == NULL) + goto out_unregister; + ubd_dev[n].fake = fake; + add_disk(fake_disk); } - if(real == NULL) return(-1); - ubd_dev[n].real = real; - + add_disk(disk); make_ide_entries(disk->disk_name); return(0); + + out_unregister: + devfs_unregister(real); + ubd_dev[n].real = NULL; + out: + return(-1); } static int ubd_config(char *str) @@ -478,24 +523,29 @@ static int ubd_config(char *str) } if(n == -1) return(0); + spin_lock(&ubd_lock); err = ubd_add(n); - if(err){ + if(err) ubd_dev[n].file = NULL; - return(err); - } + spin_unlock(&ubd_lock); - return(0); + return(err); } static int ubd_remove(char *str) { struct ubd *dev; - int n; + int n, err; - if(!isdigit(*str)) return(-1); + if(!isdigit(*str)) + return(-1); n = *str - '0'; - if(n > MAX_DEV) return(-1); + if(n > MAX_DEV) + return(-1); dev = &ubd_dev[n]; + + err = 0; + spin_lock(&ubd_lock); del_gendisk(ubd_gendisk[n]); put_disk(ubd_gendisk[n]); ubd_gendisk[n] = NULL; @@ -504,12 +554,20 @@ static int ubd_remove(char *str) put_disk(fake_gendisk[n]); fake_gendisk[n] = NULL; } - if(dev->file == NULL) return(0); - if(dev->count > 0) return(-1); - if(dev->real != NULL) devfs_unregister(dev->real); - if(dev->fake != NULL) devfs_unregister(dev->fake); + if(dev->file == NULL) + goto out; + err = -1; + if(dev->count > 0) + goto out; + if(dev->real != NULL) + devfs_unregister(dev->real); + if(dev->fake != NULL) + devfs_unregister(dev->fake); *dev = ((struct ubd) DEFAULT_UBD); - return(0); + err = 0; + out: + spin_unlock(&ubd_lock); + return(err); } static struct mc_device ubd_mc = { @@ -541,7 +599,7 @@ int ubd_init(void) return -1; } ubd_queue = BLK_DEFAULT_QUEUE(MAJOR_NR); - INIT_QUEUE(ubd_queue, do_ubd_request, &ubd_lock); + blk_init_queue(ubd_queue, do_ubd_request, &ubd_io_lock); elevator_init(ubd_queue, &elevator_noop); if(fake_major != 0){ char name[sizeof("ubd_nnn\0")]; @@ -644,22 +702,23 @@ static int ubd_open_dev(struct ubd *dev) } return(0); error: - close_fd(dev->fd); + os_close_file(dev->fd); return(err); } static int ubd_open(struct inode *inode, struct file *filp) { struct ubd *dev; - int n, offset, err; + int n, offset, err = 0; n = DEVICE_NR(inode->i_rdev); dev = &ubd_dev[n]; if(n > MAX_DEV) return -ENODEV; + offset = n << UBD_SHIFT; if(dev->is_dir == 1) - return(0); + goto out; if(dev->count == 0){ dev->openflags = dev->boot_openflags; @@ -668,16 +727,16 @@ static int ubd_open(struct inode *inode, struct file *filp) if(err){ printk(KERN_ERR "ubd%d: Can't open \"%s\": " "errno = %d\n", n, dev->file, -err); - return(err); + goto out; } - if(err) return(err); } dev->count++; if((filp->f_mode & FMODE_WRITE) && !dev->openflags.w){ if(--dev->count == 0) ubd_close(dev); - return -EROFS; + err = -EROFS; } - return(0); + out: + return(err); } static int ubd_release(struct inode * inode, struct file * file) @@ -752,13 +811,17 @@ static int prepare_request(struct request *req, struct io_thread_req *io_req) if(dev->is_dir){ strcpy(req->buffer, "HOSTFS:"); strcat(req->buffer, dev->file); + spin_lock(&ubd_io_lock); end_request(req, 1); + spin_unlock(&ubd_io_lock); return(1); } if((rq_data_dir(req) == WRITE) && !dev->openflags.w){ printk("Write attempted on readonly ubd device %d\n", n); + spin_lock(&ubd_io_lock); end_request(req, 0); + spin_unlock(&ubd_io_lock); return(1); } @@ -892,8 +955,11 @@ static int ubd_revalidate(kdev_t rdev) n = minor(rdev) >> UBD_SHIFT; dev = &ubd_dev[n]; + + err = 0; + spin_lock(&ubd_lock); if(dev->is_dir) - return(0); + goto out; err = ubd_file_size(dev, &size); if (!err) { @@ -902,7 +968,8 @@ static int ubd_revalidate(kdev_t rdev) set_capacity(fake_gendisk[n], size / 512); dev->size = size; } - + spin_unlock(&ubd_lock); + out: return err; } diff --git a/arch/um/drivers/ubd_user.c b/arch/um/drivers/ubd_user.c index 59eb46ff73a2..8a4b6f52888c 100644 --- a/arch/um/drivers/ubd_user.c +++ b/arch/um/drivers/ubd_user.c @@ -533,8 +533,12 @@ void do_io(struct io_thread_req *req) return; } +/* Changed in start_io_thread, which is serialized by being called only + * from ubd_init, which is an initcall. + */ int kernel_fd = -1; +/* Only changed by the io thread */ int io_count = 0; int io_thread(void *arg) diff --git a/arch/um/drivers/xterm.c b/arch/um/drivers/xterm.c index 9f4a6296fe66..d3f69887f3c2 100644 --- a/arch/um/drivers/xterm.c +++ b/arch/um/drivers/xterm.c @@ -44,6 +44,7 @@ void *xterm_init(char *str, int device, struct chan_opts *opts) return(data); } +/* Only changed by xterm_setup, which is a setup */ static char *terminal_emulator = "xterm"; static char *title_switch = "-T"; static char *exec_switch = "-e"; diff --git a/arch/um/include/2_5compat.h b/arch/um/include/2_5compat.h index 3802d4fafb48..6d36d9c30b91 100644 --- a/arch/um/include/2_5compat.h +++ b/arch/um/include/2_5compat.h @@ -20,8 +20,6 @@ next : NULL \ } -#define INIT_QUEUE(queue, request, lock) blk_init_queue(queue, request, lock) - #define INIT_HARDSECT(arr, maj, sizes) #define SET_PRI(task) do ; while(0) diff --git a/arch/um/include/irq_user.h b/arch/um/include/irq_user.h index 4852f2c3613e..52bed5175229 100644 --- a/arch/um/include/irq_user.h +++ b/arch/um/include/irq_user.h @@ -18,7 +18,9 @@ extern void forward_interrupts(int pid); extern void init_irq_signals(int on_sigstack); extern void forward_ipi(int fd, int pid); extern void free_irq_later(int irq, void *dev_id); - +extern int activate_ipi(int fd, int pid); +extern unsigned long irq_lock(void); +extern void irq_unlock(unsigned long flags); #endif /* diff --git a/arch/um/include/kern_util.h b/arch/um/include/kern_util.h index 07511e1e7e6e..34e107d545da 100644 --- a/arch/um/include/kern_util.h +++ b/arch/um/include/kern_util.h @@ -50,12 +50,8 @@ extern int pid_to_processor_id(int pid); extern void block_signals(void); extern void unblock_signals(void); extern void deliver_signals(void *t); -extern void lock_syscall(void); -extern void unlock_syscall(void); -extern void lock_trap(void); -extern void unlock_trap(void); -extern void lock_pid(void); -extern void unlock_pid(void); +extern int next_syscall_index(int max); +extern int next_trap_index(int max); extern void default_idle(void); extern void finish_fork(void); extern void paging_init(void); @@ -121,7 +117,7 @@ extern void arch_switch(void); extern int is_valid_pid(int pid); extern void free_irq(unsigned int, void *); extern int um_in_interrupt(void); - +extern int cpu(void); #endif /* diff --git a/arch/um/include/mconsole.h b/arch/um/include/mconsole.h index 192aab5b8e79..8f82ef7201ea 100644 --- a/arch/um/include/mconsole.h +++ b/arch/um/include/mconsole.h @@ -77,6 +77,8 @@ extern int mconsole_get_request(int fd, struct mc_request *req); extern int mconsole_notify(char *sock_name, int type, const void *data, int len); extern char *mconsole_notify_socket(void); +extern void lock_notify(void); +extern void unlock_notify(void); #endif diff --git a/arch/um/include/sigio.h b/arch/um/include/sigio.h index 23aef0140b7b..37d76e29a147 100644 --- a/arch/um/include/sigio.h +++ b/arch/um/include/sigio.h @@ -11,6 +11,8 @@ extern int register_sigio_fd(int fd); extern int read_sigio_fd(int fd); extern int add_sigio_fd(int fd, int read); extern int ignore_sigio_fd(int fd); +extern void sigio_lock(void); +extern void sigio_unlock(void); #endif diff --git a/arch/um/include/time_user.h b/arch/um/include/time_user.h index d68caf278f05..d49a34f0bee8 100644 --- a/arch/um/include/time_user.h +++ b/arch/um/include/time_user.h @@ -7,11 +7,11 @@ #define __TIME_USER_H__ extern void timer(void); -extern void get_profile_timer(void); -extern void disable_profile_timer(void); extern void switch_timers(int to_real); extern void user_time_init(void); -extern void set_timers(int set_signal); extern void idle_sleep(int secs); +extern void enable_timer(void); +extern void time_lock(void); +extern void time_unlock(void); #endif diff --git a/arch/um/kernel/exec_kern.c b/arch/um/kernel/exec_kern.c index 34a2e527a3f8..353bfa4da610 100644 --- a/arch/um/kernel/exec_kern.c +++ b/arch/um/kernel/exec_kern.c @@ -17,6 +17,7 @@ #include "tlb.h" #include "2_5compat.h" #include "os.h" +#include "time_user.h" /* See comment above fork_tramp for why sigstop is defined and used like * this @@ -28,7 +29,6 @@ static int exec_tramp(void *sig_stack) { int sig = sigstop; - block_signals(); init_new_thread(sig_stack, NULL); kill(os_getpid(), sig); return(0); @@ -62,6 +62,7 @@ void flush_thread(void) unprotect_stack((unsigned long) current->thread_info); os_usr1_process(os_getpid()); + enable_timer(); free_page(stack); protect(uml_reserved, high_physmem - uml_reserved, 1, 1, 0, 1); task_protections((unsigned long) current->thread_info); diff --git a/arch/um/kernel/exitcode.c b/arch/um/kernel/exitcode.c index 6c5c30e9c380..788f914d8510 100644 --- a/arch/um/kernel/exitcode.c +++ b/arch/um/kernel/exitcode.c @@ -8,6 +8,9 @@ #include "linux/proc_fs.h" #include "asm/uaccess.h" +/* If read and write race, the read will still atomically read a valid + * value. + */ int uml_exitcode = 0; static int read_proc_exitcode(char *page, char **start, off_t off, diff --git a/arch/um/kernel/frame.c b/arch/um/kernel/frame.c index a5d77c0ee15d..28793041426a 100644 --- a/arch/um/kernel/frame.c +++ b/arch/um/kernel/frame.c @@ -130,6 +130,7 @@ static void child_common(void *sp, int size, sighandler_t handler, int flags) os_stop_process(os_getpid()); } +/* Changed only during early boot */ struct sc_frame signal_frame_sc; struct sc_frame_raw { @@ -142,6 +143,7 @@ struct sc_frame_raw { struct arch_frame_data_raw arch; }; +/* Changed only during early boot */ static struct sc_frame_raw *raw_sc = NULL; static void sc_handler(int sig, struct sigcontext sc) @@ -163,6 +165,7 @@ static int sc_child(void *arg) return(-1); } +/* Changed only during early boot */ struct si_frame signal_frame_si; struct si_frame_raw { @@ -175,6 +178,7 @@ struct si_frame_raw { unsigned long sp; }; +/* Changed only during early boot */ static struct si_frame_raw *raw_si = NULL; static void si_handler(int sig, siginfo_t *si) diff --git a/arch/um/kernel/helper.c b/arch/um/kernel/helper.c index 324e08974d33..5d8fb7bba2b1 100644 --- a/arch/um/kernel/helper.c +++ b/arch/um/kernel/helper.c @@ -22,6 +22,7 @@ struct helper_data { int fd; }; +/* Debugging aid, changed only from gdb */ int helper_pause = 0; static void helper_hup(int sig) diff --git a/arch/um/kernel/initrd_kern.c b/arch/um/kernel/initrd_kern.c index dc6cff88b7a6..a8f7e70f1a17 100644 --- a/arch/um/kernel/initrd_kern.c +++ b/arch/um/kernel/initrd_kern.c @@ -13,6 +13,7 @@ #include "init.h" #include "os.h" +/* Changed by uml_initrd_setup, which is a setup */ static char *initrd __initdata = NULL; static int __init read_initrd(void) diff --git a/arch/um/kernel/irq.c b/arch/um/kernel/irq.c index 865ecec8d905..56454f28f03b 100644 --- a/arch/um/kernel/irq.c +++ b/arch/um/kernel/irq.c @@ -78,6 +78,7 @@ struct hw_interrupt_type no_irq_type = { end_none }; +/* Not changed */ volatile unsigned long irq_err_count; /* @@ -87,6 +88,7 @@ volatile unsigned long irq_err_count; int get_irq_list(char *buf) { int i, j; + unsigned long flags; struct irqaction * action; char *p = buf; @@ -96,9 +98,10 @@ int get_irq_list(char *buf) *p++ = '\n'; for (i = 0 ; i < NR_IRQS ; i++) { + spin_lock_irqsave(&irq_desc[i].lock, flags); action = irq_desc[i].action; if (!action) - continue; + goto end; p += sprintf(p, "%3d: ",i); #ifndef CONFIG_SMP p += sprintf(p, "%10u ", kstat_irqs(i)); @@ -113,6 +116,8 @@ int get_irq_list(char *buf) for (action=action->next; action; action = action->next) p += sprintf(p, ", %s", action->name); *p++ = '\n'; + end: + spin_unlock_irqrestore(&irq_desc[i].lock, flags); } p += sprintf(p, "\n"); #ifdef notdef @@ -548,11 +553,15 @@ void free_irq(unsigned int irq, void *dev_id) } } +/* These are initialized by sysctl_init, which is called from init/main.c */ static struct proc_dir_entry * root_irq_dir; static struct proc_dir_entry * irq_dir [NR_IRQS]; static struct proc_dir_entry * smp_affinity_entry [NR_IRQS]; -unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; +/* These are read and written as longs, so a read won't see a partial write + * even during a race. + */ +static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; #define HEX_DIGITS 8 @@ -679,6 +688,7 @@ static void register_irq_proc (unsigned int irq) smp_affinity_entry[irq] = entry; } +/* Read and written as a long */ unsigned long prof_cpu_mask = -1; void __init init_irq_proc (void) @@ -704,6 +714,21 @@ void __init init_irq_proc (void) register_irq_proc(i); } +static spinlock_t irq_spinlock = SPIN_LOCK_UNLOCKED; + +unsigned long irq_lock(void) +{ + unsigned long flags; + + spin_lock_irqsave(&irq_spinlock, flags); + return(flags); +} + +void irq_unlock(unsigned long flags) +{ + spin_unlock_irqrestore(&irq_spinlock, flags); +} + unsigned long probe_irq_on(void) { return(0); diff --git a/arch/um/kernel/irq_user.c b/arch/um/kernel/irq_user.c index 32099fc7603b..d1b66a0eb66f 100644 --- a/arch/um/kernel/irq_user.c +++ b/arch/um/kernel/irq_user.c @@ -111,40 +111,20 @@ static void maybe_sigio_broken(int fd, int type) int activate_fd(int irq, int fd, int type, void *dev_id) { - struct irq_fd *new_fd; - int pid, retval, events, err; + struct pollfd *tmp_pfd; + struct irq_fd *new_fd, *irq_fd; + unsigned long flags; + int pid, events, err, n, size; + + pid = os_getpid(); + err = os_set_fd_async(fd, pid); + if(err < 0) + goto out; - for(new_fd = active_fds;new_fd;new_fd = new_fd->next){ - if((new_fd->fd == fd) && (new_fd->type == type)){ - printk("Registering fd %d twice\n", fd); - printk("Irqs : %d, %d\n", new_fd->irq, irq); - printk("Ids : 0x%x, 0x%x\n", new_fd->id, dev_id); - return(-EIO); - } - } - pid = cpu_tasks[0].pid; - if((retval = os_set_fd_async(fd, pid)) != 0) - return(retval); new_fd = um_kmalloc(sizeof(*new_fd)); err = -ENOMEM; - if(new_fd == NULL) return(err); - pollfds_num++; - if(pollfds_num > pollfds_size){ - struct pollfd *tmp_pfd; - - tmp_pfd = um_kmalloc(pollfds_num * sizeof(pollfds[0])); - if(tmp_pfd == NULL){ - pollfds_num--; - goto out_irq; - } - if(pollfds != NULL){ - memcpy(tmp_pfd, pollfds, - sizeof(pollfds[0]) * pollfds_size); - kfree(pollfds); - } - pollfds = tmp_pfd; - pollfds_size = pollfds_num; - } + if(new_fd == NULL) + goto out; if(type == IRQ_READ) events = POLLIN | POLLPRI; else events = POLLOUT; @@ -158,29 +138,90 @@ int activate_fd(int irq, int fd, int type, void *dev_id) current_events: 0, freed : 0 } ); - *last_irq_ptr = new_fd; - last_irq_ptr = &new_fd->next; + /* Critical section - locked by a spinlock because this stuff can + * be changed from interrupt handlers. The stuff above is done + * outside the lock because it allocates memory. + */ + + /* Actually, it only looks like it can be called from interrupt + * context. The culprit is reactivate_fd, which calls + * maybe_sigio_broken, which calls write_sigio_workaround, + * which calls activate_fd. However, write_sigio_workaround should + * only be called once, at boot time. That would make it clear that + * this is called only from process context, and can be locked with + * a semaphore. + */ + flags = irq_lock(); + for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){ + if((irq_fd->fd == fd) && (irq_fd->type == type)){ + printk("Registering fd %d twice\n", fd); + printk("Irqs : %d, %d\n", irq_fd->irq, irq); + printk("Ids : 0x%x, 0x%x\n", irq_fd->id, dev_id); + goto out_free; + } + } + + n = pollfds_num; + if(n == pollfds_size){ + while(1){ + /* Here we have to drop the lock in order to call + * kmalloc, which might sleep. If something else + * came in and changed the pollfds array, we free + * the buffer and try again. + */ + irq_unlock(flags); + size = (pollfds_num + 1) * sizeof(pollfds[0]); + tmp_pfd = um_kmalloc(size); + flags = irq_lock(); + if(tmp_pfd == NULL) + goto out_unlock; + if(n == pollfds_size) + break; + kfree(tmp_pfd); + } + if(pollfds != NULL){ + memcpy(tmp_pfd, pollfds, + sizeof(pollfds[0]) * pollfds_size); + kfree(pollfds); + } + pollfds = tmp_pfd; + pollfds_size++; + } if(type == IRQ_WRITE) events = 0; - pollfds[pollfds_num - 1] = ((struct pollfd) { fd : fd, - events : events, - revents : 0 }); + pollfds[pollfds_num] = ((struct pollfd) { fd : fd, + events : events, + revents : 0 }); + pollfds_num++; + + *last_irq_ptr = new_fd; + last_irq_ptr = &new_fd->next; + irq_unlock(flags); + + /* This calls activate_fd, so it has to be outside the critical + * section. + */ maybe_sigio_broken(fd, type); return(0); - out_irq: + out_unlock: + irq_unlock(flags); + out_free: kfree(new_fd); + out: return(err); } static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg) { struct irq_fd **prev; + unsigned long flags; int i = 0; + flags = irq_lock(); prev = &active_fds; while(*prev != NULL){ if((*test)(*prev, arg)){ @@ -190,7 +231,7 @@ static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg) printk("free_irq_by_cb - mismatch between " "active_fds and pollfds, fd %d vs %d\n", (*prev)->fd, pollfds[i].fd); - return; + goto out; } memcpy(&pollfds[i], &pollfds[i + 1], (pollfds_num - i - 1) * sizeof(pollfds[0])); @@ -206,6 +247,8 @@ static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg) prev = &(*prev)->next; i++; } + out: + irq_unlock(flags); } struct irq_and_dev { @@ -242,29 +285,33 @@ static struct irq_fd *find_irq_by_fd(int fd, int irqnum, int *index_out) { struct irq_fd *irq; int i = 0; - + for(irq=active_fds; irq != NULL; irq = irq->next){ if((irq->fd == fd) && (irq->irq == irqnum)) break; i++; } if(irq == NULL){ printk("find_irq_by_fd doesn't have descriptor %d\n", fd); - return(NULL); + goto out; } if((pollfds[i].fd != -1) && (pollfds[i].fd != fd)){ printk("find_irq_by_fd - mismatch between active_fds and " "pollfds, fd %d vs %d, need %d\n", irq->fd, pollfds[i].fd, fd); - return(NULL); + irq = NULL; + goto out; } *index_out = i; + out: return(irq); } void free_irq_later(int irq, void *dev_id) { struct irq_fd *irq_fd; + unsigned long flags; + flags = irq_lock(); for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){ if((irq_fd->irq == irq) && (irq_fd->id == dev_id)) break; @@ -272,30 +319,48 @@ void free_irq_later(int irq, void *dev_id) if(irq_fd == NULL){ printk("free_irq_later found no irq, irq = %d, " "dev_id = 0x%p\n", irq, dev_id); - return; + goto out; } irq_fd->freed = 1; + out: + irq_unlock(flags); } void reactivate_fd(int fd, int irqnum) { struct irq_fd *irq; + unsigned long flags; int i; + flags = irq_lock(); irq = find_irq_by_fd(fd, irqnum, &i); - if(irq == NULL) return; + if(irq == NULL) + goto out; pollfds[i].fd = irq->fd; + + irq_unlock(flags); + + /* This calls activate_fd, so it has to be outside the critical + * section. + */ maybe_sigio_broken(fd, irq->type); + out: + irq_unlock(flags); } void deactivate_fd(int fd, int irqnum) { struct irq_fd *irq; + unsigned long flags; int i; + flags = irq_lock(); irq = find_irq_by_fd(fd, irqnum, &i); - if(irq == NULL) return; + if(irq == NULL) + goto out; pollfds[i].fd = -1; + out: + irq_unlock(flags); } void forward_ipi(int fd, int pid) @@ -313,7 +378,9 @@ void forward_ipi(int fd, int pid) void forward_interrupts(int pid) { struct irq_fd *irq; + unsigned long flags; + flags = irq_lock(); for(irq=active_fds;irq != NULL;irq = irq->next){ if(fcntl(irq->fd, F_SETOWN, pid) < 0){ int save_errno = errno; @@ -328,6 +395,7 @@ void forward_interrupts(int pid) } irq->pid = pid; } + irq_unlock(flags); } void init_irq_signals(int on_sigstack) @@ -339,10 +407,10 @@ void init_irq_signals(int on_sigstack) if(timer_irq_inited) h = (__sighandler_t) alarm_handler; else h = boot_timer_handler; - set_handler(SIGVTALRM, h, flags | SA_NODEFER | SA_RESTART, - SIGUSR1, SIGIO, SIGWINCH, -1); + set_handler(SIGVTALRM, h, flags | SA_RESTART, + SIGUSR1, SIGIO, SIGWINCH, SIGALRM, -1); set_handler(SIGIO, (__sighandler_t) sig_handler, flags | SA_RESTART, - SIGUSR1, SIGIO, SIGWINCH, -1); + SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); signal(SIGWINCH, SIG_IGN); } diff --git a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c index da7f4b318255..5a582a8de7d1 100644 --- a/arch/um/kernel/mem.c +++ b/arch/um/kernel/mem.c @@ -26,33 +26,32 @@ #include "kern.h" #include "init.h" +/* Changed during early boot */ +pgd_t swapper_pg_dir[1024]; unsigned long high_physmem; -unsigned long low_physmem; - unsigned long vm_start; unsigned long vm_end; - unsigned long highmem; - -pgd_t swapper_pg_dir[1024]; - unsigned long *empty_zero_page = NULL; - unsigned long *empty_bad_page = NULL; +/* Not modified */ const char bad_pmd_string[] = "Bad pmd in pte_alloc: %08lx\n"; extern char __init_begin, __init_end; extern long physmem_size; +/* Not changed by UML */ mmu_gather_t mmu_gathers[NR_CPUS]; +/* Changed during early boot */ int kmalloc_ok = 0; #define NREGIONS (phys_region_index(0xffffffff) - phys_region_index(0x0) + 1) struct mem_region *regions[NREGIONS] = { [ 0 ... NREGIONS - 1 ] = NULL }; #define REGION_SIZE ((0xffffffff & ~REGION_MASK) + 1) +/* Changed during early boot */ static unsigned long brk_end; static void map_cb(void *unused) @@ -108,6 +107,7 @@ void mem_init(void) } #if CONFIG_HIGHMEM +/* Changed during early boot */ pte_t *kmap_pte; pgprot_t kmap_prot; @@ -187,18 +187,22 @@ int init_maps(struct mem_region *region) return(0); } +DECLARE_MUTEX(regions_sem); + static int setup_one_range(int fd, char *driver, unsigned long start, unsigned long pfn, int len, struct mem_region *region) { int i; + down(®ions_sem); for(i = 0; i < NREGIONS; i++){ if(regions[i] == NULL) break; } if(i == NREGIONS){ printk("setup_range : no free regions\n"); - return(-1); + i = -1; + goto out; } if(fd == -1) @@ -216,6 +220,8 @@ static int setup_one_range(int fd, char *driver, unsigned long start, len : len, fd : fd } ); regions[i] = region; + out: + up(®ions_sem); return(i); } @@ -373,7 +379,8 @@ void show_mem(void) printk("%d pages swap cached\n", cached); } -unsigned long kmem_top = 0; +/* Changed during early boot */ +static unsigned long kmem_top = 0; unsigned long get_kmem_end(void) { @@ -428,8 +435,10 @@ struct page *arch_validate(struct page *page, int mask, int order) goto again; } +DECLARE_MUTEX(vm_reserved_sem); static struct list_head vm_reserved = LIST_HEAD_INIT(vm_reserved); +/* Static structures, linked in to the list in early boot */ static struct vm_reserved head = { list : LIST_HEAD_INIT(head.list), start : 0, @@ -455,7 +464,9 @@ int reserve_vm(unsigned long start, unsigned long end, void *e) { struct vm_reserved *entry = e, *reserved, *prev; struct list_head *ele; + int err; + down(&vm_reserved_sem); list_for_each(ele, &vm_reserved){ reserved = list_entry(ele, struct vm_reserved, list); if(reserved->start >= end) goto found; @@ -469,13 +480,17 @@ int reserve_vm(unsigned long start, unsigned long end, void *e) entry = kmalloc(sizeof(*entry), GFP_KERNEL); if(entry == NULL){ printk("reserve_vm : Failed to allocate entry\n"); - return(-ENOMEM); + err = -ENOMEM; + goto out; } *entry = ((struct vm_reserved) { list : LIST_HEAD_INIT(entry->list), start : start, end : end }); list_add(&entry->list, &prev->list); + err = 0; + out: + up(&vm_reserved_sem); return(0); } @@ -486,6 +501,7 @@ unsigned long get_vm(unsigned long len) unsigned long start; int err; + down(&vm_reserved_sem); list_for_each(ele, &vm_reserved){ this = list_entry(ele, struct vm_reserved, list); next = list_entry(ele->next, struct vm_reserved, list); @@ -493,8 +509,10 @@ unsigned long get_vm(unsigned long len) (this->end + len + PAGE_SIZE <= next->start)) goto found; } + up(&vm_reserved_sem); return(0); found: + up(&vm_reserved_sem); start = (unsigned long) ROUND_UP(this->end) + PAGE_SIZE; err = reserve_vm(start, start + len, NULL); if(err) return(0); @@ -533,7 +551,11 @@ struct iomem { unsigned long size; }; -struct iomem iomem_regions[NREGIONS] = { [ 0 ... NREGIONS - 1 ] = +/* iomem regions can only be added on the command line at the moment. + * Locking will be needed when they can be added via mconsole. + */ + +struct iomem iomem_regions[NREGIONS] = { [ 0 ... NREGIONS - 1 ] = { name : NULL, fd : -1, size : 0 } }; @@ -569,6 +591,7 @@ __initcall(setup_iomem); #define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) #define PFN_DOWN(x) ((x) >> PAGE_SHIFT) +/* Changed during early boot */ static struct mem_region physmem_region; static struct vm_reserved physmem_reserved; diff --git a/arch/um/kernel/mem_user.c b/arch/um/kernel/mem_user.c index 8e036687f717..af857510d17a 100644 --- a/arch/um/kernel/mem_user.c +++ b/arch/um/kernel/mem_user.c @@ -46,10 +46,9 @@ #include "mem_user.h" #include "init.h" #include "os.h" +#include "tempfile.h" -struct mem_region physmem_region; - -struct mem_region *mem_list = &physmem_region; +extern struct mem_region physmem_region; #define TEMPNAME_TEMPLATE "vm_file-XXXXXX" diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c index c3731bfd4547..d410aaa7332b 100644 --- a/arch/um/kernel/process.c +++ b/arch/um/kernel/process.c @@ -48,23 +48,23 @@ void init_new_thread(void *sig_stack, void (*usr1_handler)(int)) flags = SA_ONSTACK; } set_handler(SIGSEGV, (__sighandler_t) sig_handler, flags, - SIGUSR1, SIGIO, SIGWINCH, -1); + SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); set_handler(SIGTRAP, (__sighandler_t) sig_handler, flags, - SIGUSR1, SIGIO, SIGWINCH, -1); + SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); set_handler(SIGFPE, (__sighandler_t) sig_handler, flags, - SIGUSR1, SIGIO, SIGWINCH, -1); + SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); set_handler(SIGILL, (__sighandler_t) sig_handler, flags, - SIGUSR1, SIGIO, SIGWINCH, -1); + SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); set_handler(SIGBUS, (__sighandler_t) sig_handler, flags, - SIGUSR1, SIGIO, SIGWINCH, -1); + SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); set_handler(SIGWINCH, (__sighandler_t) sig_handler, flags, - SIGUSR1, SIGIO, SIGWINCH, -1); + SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); set_handler(SIGUSR2, (__sighandler_t) sig_handler, SA_NOMASK | flags, -1); if(usr1_handler) set_handler(SIGUSR1, usr1_handler, flags, -1); signal(SIGCHLD, SIG_IGN); signal(SIGHUP, SIG_IGN); - set_timers(1); /* XXX A bit of a race here */ + init_irq_signals(sig_stack != NULL); } diff --git a/arch/um/kernel/process_kern.c b/arch/um/kernel/process_kern.c index 8bd714887a3f..4e48f8ab3f8a 100644 --- a/arch/um/kernel/process_kern.c +++ b/arch/um/kernel/process_kern.c @@ -41,6 +41,10 @@ #include "2_5compat.h" #include "os.h" +/* This is a per-cpu array. A processor only modifies its entry and it only + * cares about its entry, so it's OK if another processor is modifying its + * entry. + */ struct cpu_task cpu_tasks[NR_CPUS] = { [0 ... NR_CPUS - 1] = { -1, NULL } }; struct task_struct *get_task(int pid, int require) @@ -86,7 +90,7 @@ int pid_to_processor_id(int pid) { int i; - for(i = 0; i < num_online_cpus(); i++){ + for(i = 0; i < ncpus; i++){ if(cpu_tasks[i].pid == pid) return(i); } return(-1); @@ -152,12 +156,19 @@ static void new_thread_handler(int sig) current->thread.regs.regs.sc = (void *) (&sig + 1); suspend_new_thread(current->thread.switch_pipe[0]); + block_signals(); +#ifdef CONFIG_SMP + schedule_tail(NULL); +#endif + enable_timer(); free_page(current->thread.temp_stack); set_cmdline("(kernel thread)"); force_flush_all(); current->thread.prev_sched = NULL; change_sig(SIGUSR1, 1); + change_sig(SIGVTALRM, 1); + change_sig(SIGPROF, 1); unblock_signals(); if(!run_kernel_thread(fn, arg, ¤t->thread.jmp)) do_exit(0); @@ -165,7 +176,9 @@ static void new_thread_handler(int sig) static int new_thread_proc(void *stack) { - block_signals(); + change_sig(SIGIO, 0); + change_sig(SIGVTALRM, 0); + change_sig(SIGPROF, 0); init_new_thread(stack, new_thread_handler); os_usr1_process(os_getpid()); return(0); @@ -204,6 +217,9 @@ void *switch_to(void *prev, void *next, void *last) unsigned long flags; int vtalrm, alrm, prof, err, cpu; char c; + /* jailing and SMP are incompatible, so this doesn't need to be + * made per-cpu + */ static int reading; from = prev; @@ -298,13 +314,14 @@ void exit_thread(void) * onto the signal frame. */ -extern int hit_me; - void finish_fork_handler(int sig) { current->thread.regs.regs.sc = (void *) (&sig + 1); suspend_new_thread(current->thread.switch_pipe[0]); + schedule_tail(NULL); + enable_timer(); + change_sig(SIGVTALRM, 1); force_flush_all(); if(current->mm != current->parent->mm) protect(uml_reserved, high_physmem - uml_reserved, 1, 1, 0, 1); @@ -313,7 +330,6 @@ void finish_fork_handler(int sig) current->thread.prev_sched = NULL; free_page(current->thread.temp_stack); - block_signals(); change_sig(SIGUSR1, 0); set_user_mode(current); } @@ -339,7 +355,9 @@ int fork_tramp(void *stack) { int sig = sigusr1; - block_signals(); + change_sig(SIGIO, 0); + change_sig(SIGVTALRM, 0); + change_sig(SIGPROF, 0); init_new_thread(stack, finish_fork_handler); kill(os_getpid(), sig); @@ -474,7 +492,7 @@ int current_pid(void) void default_idle(void) { - if(current->thread_info->cpu == 0) idle_timer(); + idle_timer(); atomic_inc(&init_mm.mm_count); current->mm = &init_mm; @@ -644,6 +662,7 @@ char *uml_strdup(char *string) return(new); } +/* Changed by jail_setup, which is a setup */ int jail = 0; int __init jail_setup(char *line, int *add) @@ -708,17 +727,14 @@ static void mprotect_kernel_mem(int w) mprotect_kernel_vm(w); } -int jail_timer_off = 0; - +/* No SMP problems since jailing and SMP are incompatible */ void unprotect_kernel_mem(void) { mprotect_kernel_mem(1); - jail_timer_off = 0; } void protect_kernel_mem(void) { - jail_timer_off = 1; mprotect_kernel_mem(0); } @@ -749,9 +765,11 @@ void set_thread_sc(void *sc) int smp_sigio_handler(void) { + int cpu = current->thread_info->cpu; #ifdef CONFIG_SMP - IPI_handler(hard_smp_processor_id()); - if (hard_smp_processor_id() != 0) return(1); + IPI_handler(cpu); + if(cpu != 0) + return(1); #endif return(0); } @@ -761,6 +779,11 @@ int um_in_interrupt(void) return(in_interrupt()); } +int cpu(void) +{ + return(current->thread_info->cpu); +} + /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically diff --git a/arch/um/kernel/sigio_kern.c b/arch/um/kernel/sigio_kern.c index fc37b78b4009..02272e623614 100644 --- a/arch/um/kernel/sigio_kern.c +++ b/arch/um/kernel/sigio_kern.c @@ -11,6 +11,7 @@ #include "sigio.h" #include "irq_user.h" +/* Protected by sigio_lock() called from write_sigio_workaround */ static int sigio_irq_fd = -1; void sigio_interrupt(int irq, void *data, struct pt_regs *unused) @@ -31,6 +32,18 @@ int write_sigio_irq(int fd) return(0); } +static spinlock_t sigio_spinlock = SPIN_LOCK_UNLOCKED; + +void sigio_lock(void) +{ + spin_lock(&sigio_spinlock); +} + +void sigio_unlock(void) +{ + spin_unlock(&sigio_spinlock); +} + /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically diff --git a/arch/um/kernel/sigio_user.c b/arch/um/kernel/sigio_user.c index fd3b70704621..740608075b7c 100644 --- a/arch/um/kernel/sigio_user.c +++ b/arch/um/kernel/sigio_user.c @@ -21,9 +21,11 @@ #include "helper.h" #include "os.h" +/* Changed during early boot */ int pty_output_sigio = 0; int pty_close_sigio = 0; +/* Used as a flag during SIGIO testing early in boot */ static int got_sigio = 0; void __init handler(int sig) @@ -151,7 +153,15 @@ void __init check_sigio(void) check_one_sigio(tty_close); } +/* Protected by sigio_lock(), also used by sigio_cleanup, which is an + * exitcall. + */ static int write_sigio_pid = -1; + +/* These arrays are initialized before the sigio thread is started, and + * the descriptors closed after it is killed. So, it can't see them change. + * On the UML side, they are changed under the sigio_lock. + */ static int write_sigio_fds[2] = { -1, -1 }; static int sigio_private[2] = { -1, -1 }; @@ -161,6 +171,9 @@ struct pollfds { int used; }; +/* Protected by sigio_lock(). Used by the sigio thread, but the UML thread + * synchronizes with it. + */ struct pollfds current_poll = { poll : NULL, size : 0, @@ -217,8 +230,6 @@ static int write_sigio_thread(void *unused) } } -/* XXX SMP locking needed here too */ - static int need_poll(int n) { if(n <= next_poll.size){ @@ -260,25 +271,31 @@ static void update_thread(void) set_signals(flags); return; fail: + sigio_lock(); if(write_sigio_pid != -1) kill(write_sigio_pid, SIGKILL); write_sigio_pid = -1; close(sigio_private[0]); close(sigio_private[1]); close(write_sigio_fds[0]); close(write_sigio_fds[1]); + sigio_unlock(); set_signals(flags); } int add_sigio_fd(int fd, int read) { - int err, i, n, events; + int err = 0, i, n, events; - for(i = 0; i < current_poll.used; i++) - if(current_poll.poll[i].fd == fd) return(0); + sigio_lock(); + for(i = 0; i < current_poll.used; i++){ + if(current_poll.poll[i].fd == fd) + goto out; + } n = current_poll.used + 1; err = need_poll(n); - if(err) return(err); + if(err) + goto out; for(i = 0; i < current_poll.used; i++) next_poll.poll[i] = current_poll.poll[i]; @@ -290,21 +307,26 @@ int add_sigio_fd(int fd, int read) events : events, revents : 0 }); update_thread(); - return(0); + out: + sigio_unlock(); + return(err); } int ignore_sigio_fd(int fd) { struct pollfd *p; - int err, i, n = 0; + int err = 0, i, n = 0; + sigio_lock(); for(i = 0; i < current_poll.used; i++){ if(current_poll.poll[i].fd == fd) break; } - if(i == current_poll.used) return(0); + if(i == current_poll.used) + goto out; err = need_poll(current_poll.used - 1); - if(err) return(err); + if(err) + goto out; for(i = 0; i < current_poll.used; i++){ p = ¤t_poll.poll[i]; @@ -312,11 +334,14 @@ int ignore_sigio_fd(int fd) } if(n == i){ printk("ignore_sigio_fd : fd %d not found\n", fd); - return(-1); + err = -1; + goto out; } update_thread(); - return(0); + out: + sigio_unlock(); + return(err); } static int setup_initial_poll(int fd) @@ -342,14 +367,15 @@ void write_sigio_workaround(void) unsigned long stack; int err; - if(write_sigio_pid != -1) return; + sigio_lock(); + if(write_sigio_pid != -1) + goto out; - /* XXX This needs SMP locking */ err = os_pipe(write_sigio_fds, 1, 1); if(err){ printk("write_sigio_workaround - os_pipe 1 failed, " "errno = %d\n", -err); - return; + goto out; } err = os_pipe(sigio_private, 1, 1); if(err){ @@ -368,6 +394,8 @@ void write_sigio_workaround(void) if(write_sigio_irq(write_sigio_fds[0])) goto out_kill; + out: + sigio_unlock(); return; out_kill: @@ -379,6 +407,7 @@ void write_sigio_workaround(void) out_close1: close(write_sigio_fds[0]); close(write_sigio_fds[1]); + sigio_unlock(); } int read_sigio_fd(int fd) diff --git a/arch/um/kernel/signal_user.c b/arch/um/kernel/signal_user.c index b5b329614493..3d1231765bb7 100644 --- a/arch/um/kernel/signal_user.c +++ b/arch/um/kernel/signal_user.c @@ -19,8 +19,6 @@ #include "sysdep/sigcontext.h" #include "sigcontext.h" -extern int kern_timer_on; - void set_sigstack(void *sig_stack, int size) { stack_t stack; @@ -65,12 +63,8 @@ static void change_signals(int type) sigset_t mask; sigemptyset(&mask); - if(type == SIG_BLOCK) kern_timer_on = 0; - else { - kern_timer_on = 1; - sigaddset(&mask, SIGVTALRM); - sigaddset(&mask, SIGALRM); - } + sigaddset(&mask, SIGVTALRM); + sigaddset(&mask, SIGALRM); sigaddset(&mask, SIGIO); sigaddset(&mask, SIGPROF); if(sigprocmask(type, &mask, NULL) < 0) @@ -97,7 +91,6 @@ static int disable_mask(sigset_t *mask) sigs = sigismember(mask, SIGIO) ? 1 << SIGIO_BIT : 0; sigs |= sigismember(mask, SIGVTALRM) ? 1 << SIGVTALRM_BIT : 0; sigs |= sigismember(mask, SIGALRM) ? 1 << SIGVTALRM_BIT : 0; - if(!kern_timer_on) sigs |= 1 << SIGVTALRM_BIT; return(sigs); } @@ -116,21 +109,27 @@ int set_signals(int disable) int ret; sigemptyset(&mask); - if(!(disable & (1 << SIGIO_BIT))) sigaddset(&mask, SIGIO); + if(!(disable & (1 << SIGIO_BIT))) + sigaddset(&mask, SIGIO); if(!(disable & (1 << SIGVTALRM_BIT))){ - kern_timer_on = 1; sigaddset(&mask, SIGVTALRM); sigaddset(&mask, SIGALRM); } if(sigprocmask(SIG_UNBLOCK, &mask, &mask) < 0) panic("Failed to enable signals"); + ret = disable_mask(&mask); + sigemptyset(&mask); - if(disable & (1 << SIGIO_BIT)) sigaddset(&mask, SIGIO); - if(disable & (1 << SIGVTALRM_BIT)) - kern_timer_on = 0; + if(disable & (1 << SIGIO_BIT)) + sigaddset(&mask, SIGIO); + if(disable & (1 << SIGVTALRM_BIT)){ + sigaddset(&mask, SIGVTALRM); + sigaddset(&mask, SIGALRM); + } if(sigprocmask(SIG_BLOCK, &mask, NULL) < 0) panic("Failed to block signals"); + return(ret); } diff --git a/arch/um/kernel/smp.c b/arch/um/kernel/smp.c index d53644005d4f..689db0e805d2 100644 --- a/arch/um/kernel/smp.c +++ b/arch/um/kernel/smp.c @@ -5,7 +5,7 @@ #include "linux/config.h" -/* CPU online map */ +/* CPU online map, set by smp_boot_cpus */ unsigned long cpu_online_map = 1; #ifdef CONFIG_SMP @@ -21,25 +21,32 @@ unsigned long cpu_online_map = 1; #include "user_util.h" #include "kern_util.h" #include "kern.h" +#include "irq_user.h" #include "os.h" -/* The 'big kernel lock' */ -spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; - -/* Per CPU bogomips and other parameters */ +/* Per CPU bogomips and other parameters + * The only piece used here is the ipi pipe, which is set before SMP is + * started and never changed. + */ struct cpuinfo_um cpu_data[NR_CPUS]; spinlock_t um_bh_lock = SPIN_LOCK_UNLOCKED; atomic_t global_bh_count; +/* Not used by UML */ unsigned char global_irq_holder = NO_PROC_ID; unsigned volatile long global_irq_lock; /* Set when the idlers are all forked */ int smp_threads_ready = 0; + +/* A statistic, can be a little off */ int num_reschedules_sent = 0; +/* Small, random number, never changed */ +unsigned long cache_decay_ticks = 5; + void smp_send_reschedule(int cpu) { write(cpu_data[cpu].ipi_pipe[1], "R", 1); @@ -83,30 +90,24 @@ void synchronize_bh(void) void smp_send_stop(void) { - printk(KERN_INFO "Stopping all CPUs\n"); -} + int i; + printk(KERN_INFO "Stopping all CPUs..."); + for(i = 0; i < num_online_cpus(); i++){ + if(i == current->thread_info->cpu) + continue; + write(cpu_data[i].ipi_pipe[1], "S", 1); + } + printk("done\n"); +} -static atomic_t smp_commenced = ATOMIC_INIT(0); +static unsigned long smp_commenced_mask; static volatile unsigned long smp_callin_map = 0; -void smp_commence(void) +static int idle_proc(void *cpup) { - printk("All CPUs are go!\n"); - - wmb(); - atomic_set(&smp_commenced, 1); -} - -static int idle_proc(void *unused) -{ - int cpu, err; - - set_current(current); - del_from_runqueue(current); - unhash_process(current); + int cpu = (int) cpup, err; - cpu = current->processor; err = os_pipe(cpu_data[cpu].ipi_pipe, 1, 1); if(err) panic("CPU#%d failed to create IPI pipe, errno = %d", cpu, @@ -115,46 +116,41 @@ static int idle_proc(void *unused) activate_ipi(cpu_data[cpu].ipi_pipe[0], current->thread.extern_pid); wmb(); - if (test_and_set_bit(current->processor, &smp_callin_map)) { - printk("huh, CPU#%d already present??\n", current->processor); + if (test_and_set_bit(cpu, &smp_callin_map)) { + printk("huh, CPU#%d already present??\n", cpu); BUG(); } - while (!atomic_read(&smp_commenced)) + while (!test_bit(cpu, &smp_commenced_mask)) cpu_relax(); - init_idle(); + set_bit(cpu, &cpu_online_map); default_idle(); return(0); } -int inited_cpus = 1; - -static int idle_thread(int (*fn)(void *), int cpu) +static struct task_struct *idle_thread(int cpu) { - struct task_struct *p; - int pid; + struct task_struct *new_task; unsigned char c; - current->thread.request.u.thread.proc = fn; - current->thread.request.u.thread.arg = NULL; - p = do_fork(CLONE_VM | CLONE_PID, 0, NULL, 0); - if(IS_ERR(p)) panic("do_fork failed in idle_thread"); - - cpu_tasks[cpu].pid = p->thread.extern_pid; - cpu_tasks[cpu].task = p; - inited_cpus++; - init_tasks[cpu] = p; - p->processor = cpu; - p->cpus_allowed = 1 << cpu; - p->cpus_runnable = p->cpus_allowed; - write(p->thread.switch_pipe[1], &c, sizeof(c)); - return(p->thread.extern_pid); + current->thread.request.u.thread.proc = idle_proc; + current->thread.request.u.thread.arg = (void *) cpu; + new_task = do_fork(CLONE_VM | CLONE_IDLETASK, 0, NULL, 0, NULL); + if(IS_ERR(new_task)) panic("do_fork failed in idle_thread"); + + cpu_tasks[cpu] = ((struct cpu_task) + { .pid = new_task->thread.extern_pid, + .task = new_task } ); + write(new_task->thread.switch_pipe[1], &c, sizeof(c)); + return(new_task); } -void smp_boot_cpus(void) +void smp_prepare_cpus(unsigned int maxcpus) { - int err; + struct task_struct *idle; + unsigned long waittime; + int err, cpu; set_bit(0, &cpu_online_map); set_bit(0, &smp_callin_map); @@ -164,46 +160,32 @@ void smp_boot_cpus(void) activate_ipi(cpu_data[0].ipi_pipe[0], current->thread.extern_pid); - if(ncpus < 1){ - printk(KERN_INFO "ncpus set to 1\n"); - ncpus = 1; - } - else if(ncpus > NR_CPUS){ - printk(KERN_INFO - "ncpus can't be greater than NR_CPUS, set to %d\n", - NR_CPUS); - ncpus = NR_CPUS; - } - - if(ncpus > 1){ - int i, pid; - - printk(KERN_INFO "Starting up other processors:\n"); - for(i=1;ineed_resched = 1; + set_tsk_need_resched(current); + break; + + case 'S': + printk("CPU#%d stopping\n", cpu); + while(1) + pause(); break; default: @@ -269,7 +257,8 @@ int smp_call_function(void (*_func)(void *info), void *_info, int nonatomic, info = _info; for (i=0;iprocessor && test_bit(i, &cpu_online_map)) + if((i != current->thread_info->cpu) && + test_bit(i, &cpu_online_map)) write(cpu_data[i].ipi_pipe[1], "C", 1); while (atomic_read(&scf_started) != cpus) diff --git a/arch/um/kernel/syscall_kern.c b/arch/um/kernel/syscall_kern.c index 1aea2a006788..b15340a6b837 100644 --- a/arch/um/kernel/syscall_kern.c +++ b/arch/um/kernel/syscall_kern.c @@ -384,6 +384,7 @@ static int check_bogosity(struct pt_regs *regs) return(0); } +/* Unlocked, I don't care if this is a bit off */ int nsyscalls = 0; extern syscall_handler_t *sys_call_table[]; @@ -417,14 +418,18 @@ long execute_syscall(void *r) spinlock_t syscall_lock = SPIN_LOCK_UNLOCKED; -void lock_syscall(void) -{ - spin_lock(&syscall_lock); -} +static int syscall_index = 0; -void unlock_syscall(void) +int next_syscall_index(int limit) { + int ret; + + spin_lock(&syscall_lock); + ret = syscall_index; + if(++syscall_index == limit) + syscall_index = 0; spin_unlock(&syscall_lock); + return(ret); } /* diff --git a/arch/um/kernel/syscall_user.c b/arch/um/kernel/syscall_user.c index 921045f804b7..5da5aefce6d8 100644 --- a/arch/um/kernel/syscall_user.c +++ b/arch/um/kernel/syscall_user.c @@ -34,21 +34,14 @@ struct { struct timeval end; } syscall_record[1024]; -int syscall_index = 0; - -extern int kern_timer_on; - void syscall_handler(int sig, struct uml_pt_regs *regs) { void *sc; long result; - int index, syscall; + int index, max, syscall; - lock_syscall(); - if(syscall_index == 1024) syscall_index = 0; - index = syscall_index; - syscall_index++; - unlock_syscall(); + max = sizeof(syscall_record)/sizeof(syscall_record[0]); + index = next_syscall_index(max); syscall = regs->syscall; sc = regs->sc; diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c index a9d4669132de..2ab57626b43f 100644 --- a/arch/um/kernel/time.c +++ b/arch/um/kernel/time.c @@ -14,33 +14,15 @@ #include "user.h" #include "process.h" #include "signal_user.h" +#include "time_user.h" extern struct timeval xtime; -void timer_handler(int sig, struct uml_pt_regs *regs) -{ - timer_irq(regs); -} - void timer(void) { gettimeofday(&xtime, NULL); } -static struct itimerval profile_interval; - -void get_profile_timer(void) -{ - getitimer(ITIMER_PROF, &profile_interval); - profile_interval.it_value = profile_interval.it_interval; -} - -void disable_profile_timer(void) -{ - struct itimerval interval = ((struct itimerval) { { 0, 0 }, { 0, 0 }}); - setitimer(ITIMER_PROF, &interval, NULL); -} - static void set_interval(int timer_type) { struct itimerval interval; @@ -53,6 +35,15 @@ static void set_interval(int timer_type) panic("setitimer failed - errno = %d\n", errno); } +void enable_timer(void) +{ + struct itimerval enable = ((struct itimerval) { { 0, 1000000/hz() }, + { 0, 1000000/hz() }}); + if(setitimer(ITIMER_VIRTUAL, &enable, NULL)) + printk("enable_timer - setitimer failed, errno = %d\n", + errno); +} + void switch_timers(int to_real) { struct itimerval disable = ((struct itimerval) { { 0, 0 }, { 0, 0 }}); @@ -79,8 +70,9 @@ void idle_timer(void) { if(signal(SIGVTALRM, SIG_IGN) == SIG_ERR) panic("Couldn't unset SIGVTALRM handler"); + set_handler(SIGALRM, (__sighandler_t) alarm_handler, - SA_NODEFER | SA_RESTART, SIGUSR1, SIGIO, SIGWINCH, -1); + SA_RESTART, SIGUSR1, SIGIO, SIGWINCH, SIGVTALRM, -1); set_interval(ITIMER_REAL); } @@ -98,28 +90,24 @@ void time_init(void) set_interval(ITIMER_VIRTUAL); } -void set_timers(int set_signal) -{ - if(set_signal) - set_interval(ITIMER_VIRTUAL); - if(setitimer(ITIMER_PROF, &profile_interval, NULL) == -1) - panic("setitimer ITIMER_PROF failed - errno = %d\n", errno); -} - struct timeval local_offset = { 0, 0 }; void do_gettimeofday(struct timeval *tv) { + time_lock(); gettimeofday(tv, NULL); timeradd(tv, &local_offset, tv); + time_unlock(); } void do_settimeofday(struct timeval *tv) { struct timeval now; + time_lock(); gettimeofday(&now, NULL); timersub(tv, &now, &local_offset); + time_unlock(); } void idle_sleep(int secs) diff --git a/arch/um/kernel/time_kern.c b/arch/um/kernel/time_kern.c index 4c96bd9637e7..14305874878b 100644 --- a/arch/um/kernel/time_kern.c +++ b/arch/um/kernel/time_kern.c @@ -27,21 +27,21 @@ int hz(void) return(HZ); } +/* Changed at early boot */ int timer_irq_inited = 0; -/* kern_timer_on and missed_ticks are modified after kernel memory has been +/* missed_ticks will be modified after kernel memory has been * write-protected, so this puts it in a section which will be left * write-enabled. */ -int __attribute__ ((__section__ (".unprotected"))) kern_timer_on = 0; -int __attribute__ ((__section__ (".unprotected"))) missed_ticks = 0; +int __attribute__ ((__section__ (".unprotected"))) missed_ticks[NR_CPUS]; void timer_irq(struct uml_pt_regs *regs) { - int ticks = missed_ticks; + int cpu = current->thread_info->cpu, ticks = missed_ticks[cpu]; if(!timer_irq_inited) return; - missed_ticks = 0; + missed_ticks[cpu] = 0; while(ticks--) do_IRQ(TIMER_IRQ, regs); } @@ -116,6 +116,27 @@ void __const_udelay(um_udelay_t usecs) for(i=0;ithread_info->cpu == 0) + timer_irq(regs); +} + +static spinlock_t timer_spinlock = SPIN_LOCK_UNLOCKED; + +void time_lock(void) +{ + spin_lock(&timer_spinlock); +} + +void time_unlock(void) +{ + spin_unlock(&timer_spinlock); +} + int __init timer_init(void) { int err; diff --git a/arch/um/kernel/trap_kern.c b/arch/um/kernel/trap_kern.c index 8f104d7a1853..158ea30937b3 100644 --- a/arch/um/kernel/trap_kern.c +++ b/arch/um/kernel/trap_kern.c @@ -171,14 +171,18 @@ void trap_init(void) spinlock_t trap_lock = SPIN_LOCK_UNLOCKED; -void lock_trap(void) -{ - spin_lock(&trap_lock); -} +static int trap_index = 0; -void unlock_trap(void) +int next_trap_index(int limit) { + int ret; + + spin_lock(&trap_lock); + ret = trap_index; + if(++trap_index == limit) + trap_index = 0; spin_unlock(&trap_lock); + return(ret); } extern int debugger_pid; @@ -209,6 +213,7 @@ static struct chan_opts opts = { tramp_stack : 0, }; +/* Accessed by the tracing thread, which automatically serializes access */ static void *xterm_data; static int xterm_fd; diff --git a/arch/um/kernel/trap_user.c b/arch/um/kernel/trap_user.c index 90a6df7a43ec..a87c94b9a367 100644 --- a/arch/um/kernel/trap_user.c +++ b/arch/um/kernel/trap_user.c @@ -70,10 +70,10 @@ void kill_child_dead(int pid) while(waitpid(pid, NULL, 0) > 0) kill(pid, SIGCONT); } +/* Changed early in boot, and then only read */ int debug = 0; int debug_stop = 1; int debug_parent = 0; - int honeypot = 0; static int signal_tramp(void *arg) @@ -90,7 +90,6 @@ static int signal_tramp(void *arg) signal(SIGUSR1, SIG_IGN); change_sig(SIGCHLD, 0); signal(SIGSEGV, (__sighandler_t) sig_handler); - set_timers(0); set_cmdline("(idle thread)"); set_init_pid(os_getpid()); proc = arg; @@ -142,33 +141,20 @@ static void sleeping_process_signal(int pid, int sig) } } -#ifdef CONFIG_SMP -#error need to make these arrays -#endif - +/* Accessed only by the tracing thread */ int debugger_pid = -1; int debugger_parent = -1; int debugger_fd = -1; int gdb_pid = -1; -struct { - unsigned long address; - int is_write; - int pid; - unsigned long sp; - int is_user; -} segfault_record[1024]; - -int segfault_index = 0; - struct { int pid; int signal; unsigned long addr; struct timeval time; -} signal_record[1024]; +} signal_record[1024][32]; -int signal_index = 0; +int signal_index[32]; int nsignals = 0; int debug_trace = 0; extern int io_nsignals, io_count, intr_count; @@ -259,36 +245,36 @@ int signals(int (*init_proc)(void *), void *sp) if(WIFEXITED(status)) ; #ifdef notdef { - printk("Child %d exited with status %d\n", pid, + printf("Child %d exited with status %d\n", pid, WEXITSTATUS(status)); } #endif else if(WIFSIGNALED(status)){ sig = WTERMSIG(status); if(sig != 9){ - printk("Child %d exited with signal %d\n", pid, + printf("Child %d exited with signal %d\n", pid, sig); } } else if(WIFSTOPPED(status)){ + proc_id = pid_to_processor_id(pid); sig = WSTOPSIG(status); - if(signal_index == 1024){ - signal_index = 0; + if(signal_index[proc_id] == 1024){ + signal_index[proc_id] = 0; last_index = 1023; } - else last_index = signal_index - 1; + else last_index = signal_index[proc_id] - 1; if(((sig == SIGPROF) || (sig == SIGVTALRM) || (sig == SIGALRM)) && - (signal_record[last_index].signal == sig) && - (signal_record[last_index].pid == pid)) - signal_index = last_index; - signal_record[signal_index].pid = pid; - gettimeofday(&signal_record[signal_index].time, NULL); + (signal_record[proc_id][last_index].signal == sig)&& + (signal_record[proc_id][last_index].pid == pid)) + signal_index[proc_id] = last_index; + signal_record[proc_id][signal_index[proc_id]].pid = pid; + gettimeofday(&signal_record[proc_id][signal_index[proc_id]].time, NULL); eip = ptrace(PTRACE_PEEKUSER, pid, PT_IP_OFFSET, 0); - signal_record[signal_index].addr = eip; - signal_record[signal_index++].signal = sig; + signal_record[proc_id][signal_index[proc_id]].addr = eip; + signal_record[proc_id][signal_index[proc_id]++].signal = sig; - proc_id = pid_to_processor_id(pid); if(proc_id == -1){ sleeping_process_signal(pid, sig); continue; @@ -413,22 +399,30 @@ __uml_setup("honeypot", uml_honeypot_setup, " UML. This implies 'jail'.\n\n" ); +/* Unlocked - don't care if this is a bit off */ int nsegfaults = 0; +struct { + unsigned long address; + int is_write; + int pid; + unsigned long sp; + int is_user; +} segfault_record[1024]; + void segv_handler(int sig, struct uml_pt_regs *regs) { struct sigcontext *context = regs->sc; - int index; + int index, max; if(regs->is_user && !SEGV_IS_FIXABLE(context)){ bad_segv(SC_FAULT_ADDR(context), SC_IP(context), SC_FAULT_WRITE(context)); return; } - lock_trap(); - index = segfault_index++; - if(segfault_index == 1024) segfault_index = 0; - unlock_trap(); + max = sizeof(segfault_record)/sizeof(segfault_record[0]); + index = next_trap_index(max); + nsegfaults++; segfault_record[index].address = SC_FAULT_ADDR(context); segfault_record[index].pid = os_getpid(); @@ -439,8 +433,6 @@ void segv_handler(int sig, struct uml_pt_regs *regs) regs->is_user, context); } -extern int kern_timer_on; - struct signal_info { void (*handler)(int, struct uml_pt_regs *); int is_irq; @@ -471,7 +463,7 @@ void sig_handler_common(int sig, struct sigcontext *sc) { struct uml_pt_regs save_regs, *r; struct signal_info *info; - int save_errno = errno, save_timer = kern_timer_on, is_user; + int save_errno = errno, is_user; unprotect_kernel_mem(); @@ -488,7 +480,6 @@ void sig_handler_common(int sig, struct sigcontext *sc) (*info->handler)(sig, r); - kern_timer_on = save_timer; if(is_user){ interrupt_end(); block_signals(); @@ -505,19 +496,15 @@ void sig_handler(int sig, struct sigcontext sc) sig_handler_common(sig, &sc); } -extern int timer_irq_inited, missed_ticks; - -extern int jail_timer_off; +extern int timer_irq_inited, missed_ticks[]; void alarm_handler(int sig, struct sigcontext sc) { int user; if(!timer_irq_inited) return; - missed_ticks++; + missed_ticks[cpu()]++; user = user_context(SC_SP(&sc)); - if(!user && !kern_timer_on) return; - if(!user && jail_timer_off) return; if(sig == SIGALRM) switch_timers(0); diff --git a/arch/um/kernel/tty_log.c b/arch/um/kernel/tty_log.c index 1472a99a7686..09aa5639007c 100644 --- a/arch/um/kernel/tty_log.c +++ b/arch/um/kernel/tty_log.c @@ -17,8 +17,8 @@ #define TTY_LOG_DIR "./" -char *tty_log_dir = TTY_LOG_DIR; - +/* Set early in boot and then unchanged */ +static char *tty_log_dir = TTY_LOG_DIR; static int tty_log_fd = -1; #define TTY_LOG_OPEN 1 diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c index 5452d3f2563c..4bc2377adba6 100644 --- a/arch/um/kernel/um_arch.c +++ b/arch/um/kernel/um_arch.c @@ -37,6 +37,11 @@ #define DEFAULT_COMMAND_LINE "root=6200" +struct cpuinfo_um boot_cpu_data = { + .loops_per_jiffy = 0, + .ipi_pipe = { -1, -1 } +}; + unsigned long thread_saved_pc(struct task_struct *task) { return(os_process_pc(task->thread.extern_pid)); @@ -119,6 +124,7 @@ static int start_kernel_proc(void *unused) #define SIZE ((CONFIG_NEST_LEVEL + CONFIG_KERNEL_HALF_GIGS) * 0x20000000) #define START (TOP - SIZE) +/* Set in main */ unsigned long host_task_size; unsigned long task_size; @@ -129,17 +135,21 @@ void set_task_sizes(int arg) task_size = START; } +/* Set in early boot */ unsigned long uml_physmem; unsigned long uml_reserved; - unsigned long start_vm; unsigned long end_vm; - int ncpus = 1; +/* Pointer set in linux_main, the array itself is private to each thread, + * and changed at address space creation time so this poses no concurrency + * problems. + */ static char *argv1_begin = NULL; static char *argv1_end = NULL; +/* Set in early boot */ static int have_root __initdata = 0; long physmem_size = 32 * 1024 * 1024; @@ -258,8 +268,9 @@ static void __init uml_postsetup(void) } extern int debug_trace; -unsigned long brk_start; +/* Set during early boot */ +unsigned long brk_start; static struct vm_reserved kernel_vm_reserved; #define MIN_VMALLOC (32 * 1024 * 1024) @@ -365,18 +376,6 @@ void __init check_bugs(void) check_sigio(); } -spinlock_t pid_lock = SPIN_LOCK_UNLOCKED; - -void lock_pid(void) -{ - spin_lock(&pid_lock); -} - -void unlock_pid(void) -{ - spin_unlock(&pid_lock); -} - /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically diff --git a/arch/um/kernel/umid.c b/arch/um/kernel/umid.c index ca6735f415ba..2ba2049510b3 100644 --- a/arch/um/kernel/umid.c +++ b/arch/um/kernel/umid.c @@ -21,9 +21,13 @@ #define UMID_LEN 64 #define UML_DIR "~/.uml/" +/* Changed by set_umid and make_umid, which are run early in boot */ static char umid[UMID_LEN] = { 0 }; + +/* Changed by set_uml_dir and make_uml_dir, which are run early in boot */ static char *uml_dir = UML_DIR; +/* Changed by set_umid */ static int umid_is_random = 1; static int umid_inited = 0; diff --git a/arch/um/kernel/user_util.c b/arch/um/kernel/user_util.c index 0445cde3cb52..d0d1891c1a20 100644 --- a/arch/um/kernel/user_util.c +++ b/arch/um/kernel/user_util.c @@ -32,6 +32,7 @@ #define COMMAND_LINE_SIZE _POSIX_ARG_MAX +/* Changed in linux_main and setup_arch, which run before SMP is started */ char saved_command_line[COMMAND_LINE_SIZE] = { 0 }; char command_line[COMMAND_LINE_SIZE] = { 0 }; diff --git a/arch/um/main.c b/arch/um/main.c index 7005f4d7bc3b..204c7a189fdd 100644 --- a/arch/um/main.c +++ b/arch/um/main.c @@ -18,15 +18,22 @@ #include "user.h" #include "init.h" +/* Set in set_stklim, which is called from main and __wrap_malloc. + * __wrap_malloc only calls it if main hasn't started. + */ unsigned long stacksizelim; +/* Set in main */ char *linux_prog; #define PGD_BOUND (4 * 1024 * 1024) #define STACKSIZE (8 * 1024 * 1024) #define THREAD_NAME_LEN (256) -char padding[THREAD_NAME_LEN] = { [ 0 ... THREAD_NAME_LEN - 2] = ' ', '\0' }; +/* Never changed */ +static char padding[THREAD_NAME_LEN] = { + [ 0 ... THREAD_NAME_LEN - 2] = ' ', '\0' +}; static void set_stklim(void) { @@ -129,7 +136,8 @@ int main(int argc, char **argv, char **envp) return(uml_exitcode); } -int allocating_monbuf = 0; +/* Changed in __wrap___monstartup and __wrap_malloc very early */ +static int allocating_monbuf = 0; #ifdef PROFILING extern void __real___monstartup (unsigned long, unsigned long); @@ -146,6 +154,7 @@ void __wrap___monstartup (unsigned long lowpc, unsigned long highpc) extern void *__real_malloc(int); extern unsigned long host_task_size; +/* Set in __wrap_malloc early */ static void *gmon_buf = NULL; void *__wrap_malloc(int size) diff --git a/arch/um/ptproxy/proxy.c b/arch/um/ptproxy/proxy.c index 78789b9403e8..2c6189ab9922 100644 --- a/arch/um/ptproxy/proxy.c +++ b/arch/um/ptproxy/proxy.c @@ -122,6 +122,7 @@ int debugger_syscall(debugger_state *debugger, pid_t child) return(0); } +/* Used by the tracing thread */ static debugger_state parent; static int parent_syscall(debugger_state *debugger, int pid); @@ -174,10 +175,7 @@ void debugger_cancelled_return(debugger_state *debugger, int result) syscall_continue(debugger->pid); } -#ifdef CONFIG_SMP -#error need to make these arrays -#endif - +/* Used by the tracing thread */ static debugger_state debugger; static debugee_state debugee; diff --git a/arch/um/sys-i386/bugs.c b/arch/um/sys-i386/bugs.c index 678b04a127c9..5b01a07368cc 100644 --- a/arch/um/sys-i386/bugs.c +++ b/arch/um/sys-i386/bugs.c @@ -15,6 +15,7 @@ #define MAXTOKEN 64 +/* Set during early boot */ int cpu_has_cmov = 1; int cpu_has_xmm = 0; diff --git a/arch/um/sys-i386/ptrace_user.c b/arch/um/sys-i386/ptrace_user.c index 45a45edd28dc..659db8a6e2d8 100644 --- a/arch/um/sys-i386/ptrace_user.c +++ b/arch/um/sys-i386/ptrace_user.c @@ -59,6 +59,7 @@ static void read_debugregs(int pid, unsigned long *regs) } } +/* Accessed only by the tracing thread */ static unsigned long kernel_debugregs[8] = { [ 0 ... 7 ] = 0 }; static int debugregs_seq = 0; diff --git a/arch/um/sys-ppc/miscthings.c b/arch/um/sys-ppc/miscthings.c index 2377b5930ea8..373061c50129 100644 --- a/arch/um/sys-ppc/miscthings.c +++ b/arch/um/sys-ppc/miscthings.c @@ -2,9 +2,6 @@ #include "linux/stddef.h" // for NULL #include "linux/elf.h" // for AT_NULL -/* unsigned int local_bh_count[NR_CPUS]; */ -unsigned long isa_io_base = 0; - /* The following function nicked from arch/ppc/kernel/process.c and * adapted slightly */ /* diff --git a/arch/um/uml.lds.S b/arch/um/uml.lds.S index f6c40ec0d143..212f614b7621 100644 --- a/arch/um/uml.lds.S +++ b/arch/um/uml.lds.S @@ -67,6 +67,9 @@ SECTIONS __setup_start = .; .setup.init : { *(.setup.init) } __setup_end = .; + __per_cpu_start = . ; + .data.percpu : { *(.data.percpu) } + __per_cpu_end = . ; __initcall_start = .; .initcall.init : { *(.initcall1.init) diff --git a/include/asm-um/cache.h b/include/asm-um/cache.h index a4962992cf86..4b134fe8504e 100644 --- a/include/asm-um/cache.h +++ b/include/asm-um/cache.h @@ -1,7 +1,10 @@ #ifndef __UM_CACHE_H #define __UM_CACHE_H +/* These are x86 numbers */ #define L1_CACHE_SHIFT 5 #define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT) +#define L1_CACHE_SHIFT_MAX 7 /* largest L1 which this arch supports */ + #endif diff --git a/include/asm-um/smp.h b/include/asm-um/smp.h index 931b1f956f17..a991b9feea3f 100644 --- a/include/asm-um/smp.h +++ b/include/asm-um/smp.h @@ -6,15 +6,30 @@ extern unsigned long cpu_online_map; #ifdef CONFIG_SMP #include "linux/config.h" +#include "linux/bitops.h" #include "asm/current.h" -#define smp_processor_id() (current->processor) +#define smp_processor_id() (current->thread_info->cpu) #define cpu_logical_map(n) (n) #define cpu_number_map(n) (n) #define PROC_CHANGE_PENALTY 15 /* Pick a number, any number */ extern int hard_smp_processor_id(void); #define NO_PROC_ID -1 +#define cpu_online(cpu) (cpu_online_map & (1<<(cpu))) + +extern int ncpus; +#define cpu_possible(cpu) (cpu < ncpus) + +extern inline unsigned int num_online_cpus(void) +{ + return(hweight32(cpu_online_map)); +} + +extern inline void smp_cpus_done(unsigned int maxcpus) +{ +} + #endif #endif diff --git a/include/asm-um/thread_info.h b/include/asm-um/thread_info.h index 494e367f4ff9..1cb0f45359f5 100644 --- a/include/asm-um/thread_info.h +++ b/include/asm-um/thread_info.h @@ -64,10 +64,14 @@ static inline struct thread_info *current_thread_info(void) #define TIF_SYSCALL_TRACE 0 /* syscall trace active */ #define TIF_SIGPENDING 1 /* signal pending */ #define TIF_NEED_RESCHED 2 /* rescheduling necessary */ +#define TIF_POLLING_NRFLAG 3 /* true if poll_idle() is polling + * TIF_NEED_RESCHED + */ #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) +#define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) #endif -- cgit v1.2.3 From eb4458e9949c046f1546b6ee72a8011c3e67d0e4 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 14 Oct 2002 22:51:54 +0200 Subject: ALSA update - ES18xx - fixed detection and initialization of opl3 and mpu401 - ENS1370 - make inclusion of *_codec.h selective for each chipset - usb audio - unified get_min_max() function to retrieve the min and max values - added the debug condition to ignore the error at get/put callbacks - quirks - use USB_DEVICE without class interface --- include/sound/version.h | 2 +- sound/isa/es18xx.c | 24 +++++---- sound/pci/ens1370.c | 6 +++ sound/usb/usbmixer.c | 134 ++++++++++++++++++++++++++++++------------------ sound/usb/usbquirks.h | 66 ++++++++++++------------ 5 files changed, 138 insertions(+), 94 deletions(-) (limited to 'include') diff --git a/include/sound/version.h b/include/sound/version.h index 11fb715148ce..9ac82df6cf79 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h. Generated automatically by configure. */ #define CONFIG_SND_VERSION "0.9.0rc3" -#define CONFIG_SND_DATE " (Sun Oct 13 15:15:37 2002 UTC)" +#define CONFIG_SND_DATE " (Mon Oct 14 16:41:26 2002 UTC)" diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c index 87ff9a8bea07..7125282c8875 100644 --- a/sound/isa/es18xx.c +++ b/sound/isa/es18xx.c @@ -1311,12 +1311,12 @@ static int __init snd_es18xx_initialize(es18xx_t *chip) if (chip->caps & ES18XX_CONTROL) { /* Hardware volume IRQ */ snd_es18xx_config_write(chip, 0x27, chip->irq); - if (chip->fm_port > SNDRV_AUTO_PORT) { + if (chip->fm_port > 0 && chip->fm_port != SNDRV_AUTO_PORT) { /* FM I/O */ snd_es18xx_config_write(chip, 0x62, chip->fm_port >> 8); snd_es18xx_config_write(chip, 0x63, chip->fm_port & 0xff); } - if (chip->mpu_port > SNDRV_AUTO_PORT) { + if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) { /* MPU-401 I/O */ snd_es18xx_config_write(chip, 0x64, chip->mpu_port >> 8); snd_es18xx_config_write(chip, 0x65, chip->mpu_port & 0xff); @@ -1404,7 +1404,7 @@ static int __init snd_es18xx_initialize(es18xx_t *chip) snd_es18xx_mixer_write(chip, 0x7A, 0x68); /* Enable and set hardware volume interrupt */ snd_es18xx_mixer_write(chip, 0x64, 0x06); - if (chip->mpu_port > SNDRV_AUTO_PORT) { + if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) { /* MPU401 share irq with audio Joystick enabled FM enabled */ @@ -2040,7 +2040,7 @@ static int __init snd_audiodrive_isapnp(int dev, struct snd_audiodrive *acard) /* skip csn and logdev initialization - already done in isapnp_configure */ isapnp_cfg_begin(pdev->bus->number, pdev->devfn); isapnp_write_byte(0x27, pdev->irq_resource[0].start); /* Hardware Volume IRQ Number */ - if (snd_mpu_port[dev] > SNDRV_AUTO_PORT) + if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) isapnp_write_byte(0x28, pdev->irq); /* MPU-401 IRQ Number */ isapnp_write_byte(0x72, pdev->irq_resource[0].start); /* second IRQ */ isapnp_cfg_end(); @@ -2147,16 +2147,18 @@ static int __init snd_audiodrive_probe(int dev) return err; } - if (snd_opl3_create(card, chip->fm_port, chip->fm_port + 2, OPL3_HW_OPL3, 0, &opl3) < 0) { - printk(KERN_ERR PFX "opl3 not detected at 0x%lx\n", chip->port); - } else { - if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { - snd_card_free(card); - return err; + if (snd_fm_port[dev] > 0 && snd_fm_port[dev] != SNDRV_AUTO_PORT) { + if (snd_opl3_create(card, chip->fm_port, chip->fm_port + 2, OPL3_HW_OPL3, 0, &opl3) < 0) { + printk(KERN_ERR PFX "opl3 not detected at 0x%lx\n", chip->fm_port); + } else { + if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } } } - if (snd_mpu_port[dev] != SNDRV_AUTO_PORT) { + if (snd_mpu_port[dev] > 0 && snd_mpu_port[dev] != SNDRV_AUTO_PORT) { if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX, chip->mpu_port, 0, irq, 0, diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 8de3b2a3a74f..36f30fb25450 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -30,8 +30,11 @@ #include #include #include +#ifdef CHIP1371 #include +#else #include +#endif #define SNDRV_GET_ID #include @@ -352,13 +355,16 @@ struct _snd_ensoniq { unsigned int rev; /* chip revision */ union { +#ifdef CHIP1371 struct { ac97_t *ac97; } es1371; +#else struct { int pclkdiv_lock; ak4531_t *ak4531; } es1370; +#endif } u; struct pci_dev *pci; diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index 7be5bbc3e8e0..6c7179967e96 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c @@ -42,6 +42,9 @@ /* */ +/* ignore error from controls - for debugging */ +/* #define IGNORE_CTL_ERROR */ + typedef struct usb_mixer_build mixer_build_t; typedef struct usb_audio_term usb_audio_term_t; typedef struct usb_mixer_elem_info usb_mixer_elem_info_t; @@ -279,8 +282,10 @@ static int get_ctl_value(usb_mixer_elem_info_t *cval, int request, int validx, i request, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, validx, cval->ctrlif | (cval->id << 8), - buf, val_len, HZ) < 0) + buf, val_len, HZ) < 0) { + snd_printdd(KERN_ERR "cannot get ctl value: req = 0x%x, idx = 0x%x, val = 0x%x, type = %d\n", request, validx, cval->ctrlif | (cval->id << 8), cval->val_type); return -EINVAL; + } *value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len)); return 0; } @@ -549,6 +554,39 @@ static void usb_mixer_elem_free(snd_kcontrol_t *kctl) * interface to ALSA control for feature/mixer units */ +/* + * retrieve the minimum and maximum values for the specified control + */ +static int get_min_max(usb_mixer_elem_info_t *cval) +{ + /* for failsafe */ + cval->min = 0; + cval->max = 1; + + if (cval->val_type == USB_MIXER_BOOLEAN || + cval->val_type == USB_MIXER_INV_BOOLEAN) { + cval->initialized = 1; + } else { + int minchn = 0; + if (cval->cmask) { + int i; + for (i = 0; i < MAX_CHANNELS; i++) + if (cval->cmask & (1 << i)) { + minchn = i + 1; + break; + } + } + if (get_ctl_value(cval, GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 || + get_ctl_value(cval, GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) { + snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n", cval->id, cval->ctrlif, cval->control, cval->id); + return -EINVAL; + } + cval->initialized = 1; + } + return 0; +} + + /* get a feature/mixer unit info */ static int mixer_ctl_feature_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) { @@ -565,23 +603,8 @@ static int mixer_ctl_feature_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t uinfo->value.integer.min = 0; uinfo->value.integer.max = 1; } else { - if (! cval->initialized) { - int minchn = 0; - if (cval->cmask) { - int i; - for (i = 0; i < MAX_CHANNELS; i++) - if (cval->cmask & (1 << i)) { - minchn = i + 1; - break; - } - } - if (get_ctl_value(cval, GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 || - get_ctl_value(cval, GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) { - snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n", cval->id, cval->ctrlif, cval->control, cval->id); - return -EINVAL; - } - cval->initialized = 1; - } + if (! cval->initialized) + get_min_max(cval); uinfo->value.integer.min = 0; uinfo->value.integer.max = cval->max - cval->min; } @@ -599,6 +622,12 @@ static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t for (c = 0; c < MAX_CHANNELS; c++) { if (cval->cmask & (1 << c)) { err = get_cur_mix_value(cval, c + 1, &val); +#ifdef IGNORE_CTL_ERROR + if (err < 0) { + ucontrol->value.integer.value[0] = cval->min; + return 0; + } +#endif if (err < 0) { snd_printd(KERN_ERR "cannot get current value for control %d ch %d: err = %d\n", cval->control, c + 1, err); return err; @@ -611,6 +640,12 @@ static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t } else { /* master channel */ err = get_cur_mix_value(cval, 0, &val); +#ifdef IGNORE_CTL_ERROR + if (err < 0) { + ucontrol->value.integer.value[0] = cval->min; + return 0; + } +#endif if (err < 0) { snd_printd(KERN_ERR "cannot get current value for control %d master ch: err = %d\n", cval->control, err); return err; @@ -633,6 +668,10 @@ static int mixer_ctl_feature_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t for (c = 0; c < MAX_CHANNELS; c++) { if (cval->cmask & (1 << c)) { err = get_cur_mix_value(cval, c + 1, &oval); +#ifdef IGNORE_CTL_ERROR + if (err < 0) + return 0; +#endif if (err < 0) return err; val = ucontrol->value.integer.value[cnt]; @@ -647,6 +686,10 @@ static int mixer_ctl_feature_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t } else { /* master channel */ err = get_cur_mix_value(cval, 0, &oval); +#ifdef IGNORE_CTL_ERROR + if (err < 0) + return 0; +#endif if (err < 0) return err; val = ucontrol->value.integer.value[0]; @@ -680,7 +723,6 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, int nameid = desc[desc[0] - 1]; snd_kcontrol_t *kctl; usb_mixer_elem_info_t *cval; - int minchn = 0; control++; /* change from zero-based to 1-based value */ @@ -705,26 +747,13 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, else { int i, c = 0; for (i = 0; i < 16; i++) - if (ctl_mask & (1 << i)) { - if (! minchn) - minchn = i + 1; + if (ctl_mask & (1 << i)) c++; - } cval->channels = c; } /* get min/max values */ - if (cval->val_type == USB_MIXER_BOOLEAN || - cval->val_type == USB_MIXER_INV_BOOLEAN) { - cval->max = 1; - cval->initialized = 1; - } else { - if (get_ctl_value(cval, GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 || - get_ctl_value(cval, GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) - snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n", cval->id, cval->ctrlif, control, unitid); - else - cval->initialized = 1; - } + get_min_max(cval); kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); if (! kctl) { @@ -860,7 +889,6 @@ static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc, int i, len; snd_kcontrol_t *kctl; usb_audio_term_t iterm; - int minchn = 0; cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL); if (! cval) @@ -878,17 +906,11 @@ static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc, if (check_matrix_bitmap(desc + 9 + num_ins, in_ch, i, num_outs)) { cval->cmask |= (1 << i); cval->channels++; - if (! minchn) - minchn = i + 1; } } /* get min/max values */ - if (get_ctl_value(cval, GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 || - get_ctl_value(cval, GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) - snd_printd(KERN_ERR "cannot get min/max values for mixer (id %d)\n", unitid); - else - cval->initialized = 1; + get_min_max(cval); kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); if (! kctl) { @@ -944,6 +966,12 @@ static int mixer_ctl_procunit_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t int err, val; err = get_cur_ctl_value(cval, cval->control, &val); +#ifdef IGNORE_CTL_ERROR + if (err < 0) { + ucontrol->value.integer.value[0] = cval->min; + return 0; + } +#endif if (err < 0) return err; val = get_relative_value(cval, val); @@ -958,6 +986,10 @@ static int mixer_ctl_procunit_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t int val, oval, err; err = get_cur_ctl_value(cval, cval->control, &oval); +#ifdef IGNORE_CTL_ERROR + if (err < 0) + return 0; +#endif if (err < 0) return err; val = ucontrol->value.integer.value[0]; @@ -1099,13 +1131,7 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char cval->channels = 1; /* get min/max values */ - if (get_ctl_value(cval, GET_MAX, cval->control, &cval->max) < 0 || - get_ctl_value(cval, GET_MIN, cval->control, &cval->min) < 0) - snd_printd(KERN_ERR "cannot get min/max values for proc/ext control=%d, id=%d\n", cval->control, unitid); - else if (cval->max <= cval->min) - snd_printd(KERN_ERR "invalid min/max values (%d/%d) for proc/ext unit control=%d, id=%d\n", cval->min, cval->max, cval->control, unitid); - else - cval->initialized = 1; + get_min_max(cval); kctl = snd_ctl_new1(&mixer_procunit_ctl, cval); if (! kctl) { @@ -1183,6 +1209,12 @@ static int mixer_ctl_selector_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t int val, err; err = get_cur_ctl_value(cval, 0, &val); +#ifdef IGNORE_CTL_ERROR + if (err < 0) { + ucontrol->value.enumerated.item[0] = 0; + return 0; + } +#endif if (err < 0) return err; val = get_relative_value(cval, val); @@ -1197,6 +1229,10 @@ static int mixer_ctl_selector_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t int val, oval, err; err = get_cur_ctl_value(cval, 0, &oval); +#ifdef IGNORE_CTL_ERROR + if (err < 0) + return 0; +#endif if (err < 0) return err; val = ucontrol->value.enumerated.item[0]; diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h index 87348c929f59..64f8ee0bea35 100644 --- a/sound/usb/usbquirks.h +++ b/sound/usb/usbquirks.h @@ -38,7 +38,7 @@ /* Yamaha devices */ { - USB_DEVICE_VENDOR_SPEC(0x0499, 0x1000), + USB_DEVICE(0x0499, 0x1000), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Yamaha", .product_name = "UX256", @@ -47,7 +47,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0499, 0x1001), + USB_DEVICE(0x0499, 0x1001), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Yamaha", .product_name = "MU1000", @@ -56,7 +56,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0499, 0x1002), + USB_DEVICE(0x0499, 0x1002), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Yamaha", .product_name = "MU2000", @@ -65,7 +65,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0499, 0x1003), + USB_DEVICE(0x0499, 0x1003), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Yamaha", .product_name = "MU500", @@ -74,7 +74,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0499, 0x1004), + USB_DEVICE(0x0499, 0x1004), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Yamaha", .product_name = "UW500", @@ -83,7 +83,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0499, 0x1005), + USB_DEVICE(0x0499, 0x1005), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Yamaha", .product_name = "MOTIF6", @@ -92,7 +92,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0499, 0x1006), + USB_DEVICE(0x0499, 0x1006), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Yamaha", .product_name = "MOTIF7", @@ -101,7 +101,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0499, 0x1007), + USB_DEVICE(0x0499, 0x1007), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Yamaha", .product_name = "MOTIF8", @@ -110,7 +110,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0499, 0x1008), + USB_DEVICE(0x0499, 0x1008), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Yamaha", .product_name = "UX96", @@ -119,7 +119,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0499, 0x1009), + USB_DEVICE(0x0499, 0x1009), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Yamaha", .product_name = "UX16", @@ -128,7 +128,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0499, 0x100a), + USB_DEVICE(0x0499, 0x100a), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Yamaha", .product_name = "EOS BX", @@ -137,7 +137,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0499, 0x100e), + USB_DEVICE(0x0499, 0x100e), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Yamaha", .product_name = "S08", @@ -146,7 +146,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0499, 0x100f), + USB_DEVICE(0x0499, 0x100f), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Yamaha", .product_name = "CLP-150", @@ -155,7 +155,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0499, 0x1010), + USB_DEVICE(0x0499, 0x1010), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Yamaha", .product_name = "CLP-170", @@ -177,7 +177,7 @@ * class-specific descriptors. */ { - USB_DEVICE_VENDOR_SPEC(0x0582, 0x0000), + USB_DEVICE(0x0582, 0x0000), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Roland", .product_name = "UA-100", @@ -191,7 +191,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0582, 0x0002), + USB_DEVICE(0x0582, 0x0002), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", .product_name = "UM-4", @@ -205,7 +205,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0582, 0x0003), + USB_DEVICE(0x0582, 0x0003), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Roland", .product_name = "SC-8850", @@ -219,7 +219,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0582, 0x0004), + USB_DEVICE(0x0582, 0x0004), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Roland", .product_name = "U-8", @@ -233,7 +233,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0582, 0x0005), + USB_DEVICE(0x0582, 0x0005), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", .product_name = "UM-2", @@ -247,7 +247,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0582, 0x0007), + USB_DEVICE(0x0582, 0x0007), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Roland", .product_name = "SC-8820", @@ -261,7 +261,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0582, 0x0008), + USB_DEVICE(0x0582, 0x0008), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Roland", .product_name = "PC-300", @@ -275,7 +275,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0582, 0x0009), + USB_DEVICE(0x0582, 0x0009), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", .product_name = "UM-1", @@ -289,7 +289,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0582, 0x000b), + USB_DEVICE(0x0582, 0x000b), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Roland", .product_name = "SK-500", @@ -303,7 +303,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0582, 0x000c), + USB_DEVICE(0x0582, 0x000c), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Roland", .product_name = "SC-D70", @@ -317,7 +317,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0582, 0x0012), + USB_DEVICE(0x0582, 0x0012), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Roland", .product_name = "XV-5050", @@ -331,7 +331,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0582, 0x0014), + USB_DEVICE(0x0582, 0x0014), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", .product_name = "UM-880", @@ -345,7 +345,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0582, 0x0016), + USB_DEVICE(0x0582, 0x0016), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", .product_name = "SD-90", @@ -359,7 +359,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0582, 0x0023), + USB_DEVICE(0x0582, 0x0023), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", .product_name = "UM-550", @@ -373,7 +373,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0582, 0x0027), + USB_DEVICE(0x0582, 0x0027), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", .product_name = "SD-20", @@ -387,7 +387,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0582, 0x0029), + USB_DEVICE(0x0582, 0x0029), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", .product_name = "SD-80", @@ -401,7 +401,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0582, 0x002b), + USB_DEVICE(0x0582, 0x002b), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", .product_name = "UA-700", @@ -467,7 +467,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0763, 0x2001), + USB_DEVICE(0x0763, 0x2001), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "M-Audio", .product_name = "Quattro", @@ -477,7 +477,7 @@ } }, { - USB_DEVICE_VENDOR_SPEC(0x0763, 0x2003), + USB_DEVICE(0x0763, 0x2003), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "M-Audio", .product_name = "AudioPhile", -- cgit v1.2.3 From 0ebd6ce0f49a6a8a7623ede60befa51a575271ce Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 14 Oct 2002 23:25:35 +0100 Subject: [MTD] Update 2.5 MTD code from MTD CVS and ARM tree This cset updates the 2.5 MTD code from the MTD CVS. David Woodhouse is happy with me sending this. Summary of changes: - Add MTD device concatenation support module. - Bootldr MTD partition parsing is obsolete, replaced by command-line based partition information. - Add support for ARM map drivers: AUTCPU12, Ceiva, Camelot, Fortunet, edb7312, Impa7, PCI - Add support for PCMCIA memory cards - Update help texts for: Ocelot, ITE QED-4N-S01B, Flaga Please note that this does not completely synchronise the 2.5 kernel tree with MTD CVS. --- drivers/mtd/Config.help | 60 +- drivers/mtd/Config.in | 5 +- drivers/mtd/Makefile | 11 +- drivers/mtd/bootldr.c | 214 ----- drivers/mtd/cmdline.c | 343 ++++++++ drivers/mtd/maps/Config.help | 109 ++- drivers/mtd/maps/Config.in | 12 +- drivers/mtd/maps/Makefile | 20 +- drivers/mtd/maps/autcpu12-nvram.c | 179 +++++ drivers/mtd/maps/ceiva.c | 408 ++++++++++ drivers/mtd/maps/dc21285.c | 25 +- drivers/mtd/maps/edb7312.c | 202 +++++ drivers/mtd/maps/epxa10db-flash.c | 233 ++++++ drivers/mtd/maps/fortunet.c | 309 ++++++++ drivers/mtd/maps/impa7.c | 234 ++++++ drivers/mtd/maps/iq80310.c | 5 +- drivers/mtd/maps/pci.c | 385 +++++++++ drivers/mtd/maps/pcmciamtd.c | 893 +++++++++++++++++++++ drivers/mtd/maps/sa1100-flash.c | 1554 +++++++++++++++++++++++++------------ drivers/mtd/mtdconcat.c | 675 ++++++++++++++++ include/linux/mtd/concat.h | 23 + 21 files changed, 5130 insertions(+), 769 deletions(-) delete mode 100644 drivers/mtd/bootldr.c create mode 100644 drivers/mtd/cmdline.c create mode 100644 drivers/mtd/maps/autcpu12-nvram.c create mode 100644 drivers/mtd/maps/ceiva.c create mode 100644 drivers/mtd/maps/edb7312.c create mode 100644 drivers/mtd/maps/epxa10db-flash.c create mode 100644 drivers/mtd/maps/fortunet.c create mode 100644 drivers/mtd/maps/impa7.c create mode 100644 drivers/mtd/maps/pci.c create mode 100644 drivers/mtd/maps/pcmciamtd.c create mode 100644 drivers/mtd/mtdconcat.c create mode 100644 include/linux/mtd/concat.h (limited to 'include') diff --git a/drivers/mtd/Config.help b/drivers/mtd/Config.help index 822dc7424d34..83e9b5c7d44b 100644 --- a/drivers/mtd/Config.help +++ b/drivers/mtd/Config.help @@ -21,36 +21,62 @@ CONFIG_MTD_PARTITIONS devices. Partitioning on NFTL 'devices' is a different - that's the 'normal' form of partitioning used on a block device. +CONFIG_MTD_CONCAT + Support for concatenating several MTD devices into a single + (virtual) one. This allows you to have -for example- a JFFS(2) + file system spanning multiple physical flash chips. If unsure, + say 'Y'. + CONFIG_MTD_REDBOOT_PARTS RedBoot is a ROM monitor and bootloader which deals with multiple - 'images' in flash devices by putting a table in the last erase block - of the device, similar to a partition table, which gives the - offsets, lengths and names of all the images stored in the flash. + 'images' in flash devices by putting a table in the last erase + block of the device, similar to a partition table, which gives + the offsets, lengths and names of all the images stored in the + flash. If you need code which can detect and parse this table, and register MTD 'partitions' corresponding to each image in the table, enable - this option. + this option. You will still need the parsing functions to be called by the driver - for your particular device. It won't happen automatically. The - SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for + for your particular device. It won't happen automatically. The + SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for example. -CONFIG_MTD_BOOTLDR_PARTS - The Compaq bootldr deals with multiple 'images' in flash devices - by putting a table in one of the first erase blocks of the device, - similar to a partition table, which gives the offsets, lengths and - names of all the images stored in the flash. - - If you need code which can detect and parse this table, and register - MTD 'partitions' corresponding to each image in the table, enable - this option. - +CONFIG_MTD_CMDLINE_PARTS + Allow generic configuration of the MTD paritition tables via the kernel + command line. Multiple flash resources are supported for hardware where + different kinds of flash memory are available. + You will still need the parsing functions to be called by the driver for your particular device. It won't happen automatically. The SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for example. + The format for the command line is as follows: + + mtdparts=[; := :[,] + := [@offset][][ro] + := unique id used in mapping driver/device + := standard linux memsize OR "-" to denote all + remaining space + := (NAME) + + Due to the way Linux handles the command line, no spaces are + allowed in the partition definition, including mtd id's and partition + names. + + Examples: + + 1 flash resource (mtd-id "sa1100"), with 1 single writable partition: + mtdparts=sa1100:- + + Same flash, but 2 named partitions, the first one being read-only: + mtdparts=sa1100:256k(ARMboot)ro,-(root) + + If unsure, say 'N'. + CONFIG_MTD_AFS_PARTS The ARM Firmware Suite allows the user to divide flash devices into multiple 'images'. Each such image has a header containing its name @@ -61,7 +87,7 @@ CONFIG_MTD_AFS_PARTS enable this option. You will still need the parsing functions to be called by the driver - for your particular device. It won't happen automatically. The + for your particular device. It won't happen automatically. The 'armflash' map driver (CONFIG_MTD_ARMFLASH) does this, for example. CONFIG_MTD_DEBUG_VERBOSE diff --git a/drivers/mtd/Config.in b/drivers/mtd/Config.in index 797f79667844..7e3d3ffd2983 100644 --- a/drivers/mtd/Config.in +++ b/drivers/mtd/Config.in @@ -1,5 +1,5 @@ -# $Id: Config.in,v 1.71 2001/10/03 11:38:38 dwmw2 Exp $ +# $Id: Config.in,v 1.74 2002/04/23 13:52:14 mag Exp $ mainmenu_option next_comment comment 'Memory Technology Devices (MTD)' @@ -12,9 +12,10 @@ if [ "$CONFIG_MTD" = "y" -o "$CONFIG_MTD" = "m" ]; then int ' Debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_MTD_DEBUG_VERBOSE 0 fi dep_tristate ' MTD partitioning support' CONFIG_MTD_PARTITIONS $CONFIG_MTD + dep_tristate ' MTD concatenating support' CONFIG_MTD_CONCAT $CONFIG_MTD dep_tristate ' RedBoot partition table parsing' CONFIG_MTD_REDBOOT_PARTS $CONFIG_MTD_PARTITIONS + dep_tristate ' Command line partition table parsing' CONFIG_MTD_CMDLINE_PARTS $CONFIG_MTD_PARTITIONS if [ "$CONFIG_ARM" = "y" ]; then - dep_tristate ' Compaq bootldr partition table parsing' CONFIG_MTD_BOOTLDR_PARTS $CONFIG_MTD_PARTITIONS dep_tristate ' ARM Firmware Suite partition parsing' CONFIG_MTD_AFS_PARTS $CONFIG_MTD_PARTITIONS fi diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 4b8108198e7e..7ec5dfbb2501 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -1,12 +1,12 @@ # # Makefile for the memory technology device drivers. # -# -# $Id: Makefile,v 1.63 2001/06/13 09:43:07 dwmw2 Exp $ +# Based on: +# $Id: Makefile,v 1.66 2002/04/23 13:52:14 mag Exp $ -export-objs := mtdcore.o mtdpart.o redboot.o bootldr.o afs.o +export-objs := mtdcore.o mtdpart.o redboot.o cmdline.o afs.o mtdconcat.o -obj-y += chips/ maps/ devices/ nand/ +obj-y += chips/ maps/ devices/ nand/ # *** BIG UGLY NOTE *** # @@ -26,9 +26,10 @@ obj-y += chips/ maps/ devices/ nand/ # Core functionality. obj-$(CONFIG_MTD) += mtdcore.o +obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o obj-$(CONFIG_MTD_PARTITIONS) += mtdpart.o obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o -obj-$(CONFIG_MTD_BOOTLDR_PARTS) += bootldr.o +obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdline.o obj-$(CONFIG_MTD_AFS_PARTS) += afs.o # 'Users' - code which presents functionality to userspace. diff --git a/drivers/mtd/bootldr.c b/drivers/mtd/bootldr.c deleted file mode 100644 index 43fcd6bea8b8..000000000000 --- a/drivers/mtd/bootldr.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Read flash partition table from Compaq Bootloader - * - * Copyright 2001 Compaq Computer Corporation. - * - * $Id: bootldr.c,v 1.6 2001/10/02 15:05:11 dwmw2 Exp $ - * - * Use consistent with the GNU GPL is permitted, - * provided that this copyright notice is - * preserved in its entirety in all copies and derived works. - * - * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, - * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS - * FITNESS FOR ANY PARTICULAR PURPOSE. - * - */ - -/* - * Maintainer: Jamey Hicks (jamey.hicks@compaq.com) - */ - -#include -#include - -#include -#include -#include -#include - -#define FLASH_PARTITION_NAMELEN 32 -enum LFR_FLAGS { - LFR_SIZE_PREFIX = 1, /* prefix data with 4-byte size */ - LFR_PATCH_BOOTLDR = 2, /* patch bootloader's 0th instruction */ - LFR_KERNEL = 4, /* add BOOTIMG_MAGIC, imgsize and VKERNEL_BASE to head of programmed region (see bootldr.c) */ - LFR_EXPAND = 8 /* expand partition size to fit rest of flash */ -}; - -// the tags are parsed too early to malloc or alloc_bootmem so we'll fix it -// for now -#define MAX_NUM_PARTITIONS 8 -typedef struct FlashRegion { - char name[FLASH_PARTITION_NAMELEN]; - unsigned long base; - unsigned long size; - enum LFR_FLAGS flags; -} FlashRegion; - -typedef struct BootldrFlashPartitionTable { - int magic; /* should be filled with 0x646c7470 (btlp) BOOTLDR_PARTITION_MAGIC */ - int npartitions; - struct FlashRegion partition[8]; -} BootldrFlashPartitionTable; - -#define BOOTLDR_MAGIC 0x646c7462 /* btld: marks a valid bootldr image */ -#define BOOTLDR_PARTITION_MAGIC 0x646c7470 /* btlp: marks a valid bootldr partition table in params sector */ - -#define BOOTLDR_MAGIC_OFFSET 0x20 /* offset 0x20 into the bootldr */ -#define BOOTCAP_OFFSET 0X30 /* offset 0x30 into the bootldr */ - -#define BOOTCAP_WAKEUP (1<<0) -#define BOOTCAP_PARTITIONS (1<<1) /* partition table stored in params sector */ -#define BOOTCAP_PARAMS_AFTER_BOOTLDR (1<<2) /* params sector right after bootldr sector(s), else in last sector */ - -static struct BootldrFlashPartitionTable Table; -static struct BootldrFlashPartitionTable *partition_table = NULL; - - -int parse_bootldr_partitions(struct mtd_info *master, struct mtd_partition **pparts) -{ - struct mtd_partition *parts; - int ret, retlen, i; - int npartitions = 0; - long partition_table_offset; - long bootmagic = 0; - long bootcap = 0; - int namelen = 0; - - char *names; - -#if 0 - /* verify bootldr magic */ - ret = master->read(master, BOOTLDR_MAGIC_OFFSET, sizeof(long), &retlen, (void *)&bootmagic); - if (ret) - goto out; - if (bootmagic != BOOTLDR_MAGIC) - goto out; - /* see if bootldr supports partition tables and where to find the partition table */ - ret = master->read(master, BOOTCAP_OFFSET, sizeof(long), &retlen, (void *)&bootcap); - if (ret) - goto out; - - if (!(bootcap & BOOTCAP_PARTITIONS)) - goto out; - if (bootcap & BOOTCAP_PARAMS_AFTER_BOOTLDR) - partition_table_offset = master->erasesize; - else - partition_table_offset = master->size - master->erasesize; - - printk(__FUNCTION__ ": partition_table_offset=%#lx\n", partition_table_offset); - printk(__FUNCTION__ ": ptable_addr=%#lx\n", ptable_addr); - - - /* Read the partition table */ - partition_table = (struct BootldrFlashPartitionTable *)kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!partition_table) - return -ENOMEM; - - ret = master->read(master, partition_table_offset, - PAGE_SIZE, &retlen, (void *)partition_table); - if (ret) - goto out; - -#endif - if (!partition_table) - return -ENOMEM; - - - printk(__FUNCTION__ ": magic=%#x\n", partition_table->magic); - printk(__FUNCTION__ ": numPartitions=%#x\n", partition_table->npartitions); - - - /* check for partition table magic number */ - if (partition_table->magic != BOOTLDR_PARTITION_MAGIC) - goto out; - npartitions = (partition_table->npartitions > MAX_NUM_PARTITIONS)? - MAX_NUM_PARTITIONS:partition_table->npartitions; - - printk(__FUNCTION__ ": npartitions=%#x\n", npartitions); - - for (i = 0; i < npartitions; i++) { - namelen += strlen(partition_table->partition[i].name) + 1; - } - - parts = kmalloc(sizeof(*parts)*npartitions + namelen, GFP_KERNEL); - if (!parts) { - ret = -ENOMEM; - goto out; - } - names = (char *)&parts[npartitions]; - memset(parts, 0, sizeof(*parts)*npartitions + namelen); - - - - // from here we use the partition table - for (i = 0; i < npartitions; i++) { - struct FlashRegion *partition = &partition_table->partition[i]; - const char *name = partition->name; - parts[i].name = names; - names += strlen(name) + 1; - strcpy(parts[i].name, name); - - if (partition->flags & LFR_EXPAND) - parts[i].size = MTDPART_SIZ_FULL; - else - parts[i].size = partition->size; - parts[i].offset = partition->base; - parts[i].mask_flags = 0; - - printk(" partition %s o=%x s=%x\n", - parts[i].name, parts[i].offset, parts[i].size); - - } - - ret = npartitions; - *pparts = parts; - - out: -#if 0 - if (partition_table) - kfree(partition_table); -#endif - - return ret; -} - - -static int __init parse_tag_ptable(const struct tag *tag) -{ - char buf[128]; - int i; - int j; - - partition_table = &Table; - -#ifdef CONFIG_DEBUG_LL - sprintf(buf,"ptable: magic = = 0x%lx npartitions= %d \n", - tag->u.ptable.magic,tag->u.ptable.npartitions); - printascii(buf); - - for (i=0; iu.ptable.npartitions; i++){ - sprintf(buf,"ptable: partition name = %s base= 0x%lx size= 0x%lx flags= 0x%lx\n", - (char *) (&tag->u.ptable.partition[i].name[0]), - tag->u.ptable.partition[i].base, - tag->u.ptable.partition[i].size, - tag->u.ptable.partition[i].flags); - printascii(buf); - } -#endif - - memcpy((void *)partition_table,(void *) (&(tag->u.ptable)),sizeof(partition_table) + - sizeof(struct FlashRegion)*tag->u.ptable.npartitions); - - - return 0; -} - -__tagtable(ATAG_PTABLE, parse_tag_ptable); - -EXPORT_SYMBOL(parse_bootldr_partitions); - - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Compaq Computer Corporation"); -MODULE_DESCRIPTION("Parsing code for Compaq bootldr partitions"); diff --git a/drivers/mtd/cmdline.c b/drivers/mtd/cmdline.c new file mode 100644 index 000000000000..4d92157f46de --- /dev/null +++ b/drivers/mtd/cmdline.c @@ -0,0 +1,343 @@ +/* + * $Id: cmdline.c,v 1.4 2002/09/13 01:18:38 jamey Exp $ + * + * Read flash partition table from command line + * + * Copyright 2002 SYSGO Real-Time Solutions GmbH + * + * The format for the command line is as follows: + * + * mtdparts=[; := :[,] + * := [@offset][][ro] + * := unique id used in mapping driver/device + * := standard linux memsize OR "-" to denote all remaining space + * := '(' NAME ')' + * + * Examples: + * + * 1 NOR Flash, with 1 single writable partition: + * edb7312-nor:- + * + * 1 NOR Flash with 2 partitions, 1 NAND with one + * edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home) + */ + +#include +#include + +#include +#include +#include +#include + +/* error message prefix */ +#define ERRP "mtd: " + +/* debug macro */ +#if 0 +#define dbg(x) do { printk("DEBUG-CMDLINE-PART: "); printk x; } while(0) +#else +#define dbg(x) +#endif + + +/* special size referring to all the remaining space in a partition */ +#define SIZE_REMAINING 0xffffffff + +struct cmdline_mtd_partition { + struct cmdline_mtd_partition *next; + char *mtd_id; + int num_parts; + struct mtd_partition *parts; +}; + +/* mtdpart_setup() parses into here */ +static struct cmdline_mtd_partition *partitions; + +/* the command line passed to mtdpart_setupd() */ +static char *cmdline; +static int cmdline_parsed = 0; + +/* + * Parse one partition definition for an MTD. Since there can be many + * comma separated partition definitions, this function calls itself + * recursively until no more partition definitions are found. Nice side + * effect: the memory to keep the mtd_partition structs and the names + * is allocated upon the last definition being found. At that point the + * syntax has been verified ok. + */ +static struct mtd_partition * newpart(char *s, + char **retptr, + int *num_parts, + int this_part, + unsigned char **extra_mem_ptr, + int extra_mem_size) +{ + struct mtd_partition *parts; + unsigned long size; + unsigned long offset = 0; + char *name; + int name_len; + unsigned char *extra_mem; + char delim; + unsigned int mask_flags; + + /* fetch the partition size */ + if (*s == '-') + { /* assign all remaining space to this partition */ + size = SIZE_REMAINING; + s++; + } + else + { + size = memparse(s, &s); + if (size < PAGE_SIZE) + { + printk(KERN_ERR ERRP "partition size too small (%lx)\n", size); + return 0; + } + } + + /* fetch partition name and flags */ + mask_flags = 0; /* this is going to be a regular partition */ + delim = 0; + /* check for offset */ + if (*s == '@') + { + s++; + offset = memparse(s, &s); + } + /* now look for name */ + if (*s == '(') + { + delim = ')'; + } + if (delim) + { + char *p; + + name = ++s; + if ((p = strchr(name, delim)) == 0) + { + printk(KERN_ERR ERRP "no closing %c found in partition name\n", delim); + return 0; + } + name_len = p - name; + s = p + 1; + } + else + { + name = NULL; + name_len = 13; /* Partition_000 */ + } + + /* record name length for memory allocation later */ + extra_mem_size += name_len + 1; + + /* test for options */ + if (strncmp(s, "ro", 2) == 0) + { + mask_flags |= MTD_WRITEABLE; + s += 2; + } + + /* test if more partitions are following */ + if (*s == ',') + { + if (size == SIZE_REMAINING) + { + printk(KERN_ERR ERRP "no partitions allowed after a fill-up partition\n"); + return 0; + } + /* more partitions follow, parse them */ + if ((parts = newpart(s + 1, &s, num_parts, + this_part + 1, &extra_mem, extra_mem_size)) == 0) + return 0; + } + else + { /* this is the last partition: allocate space for all */ + int alloc_size; + + *num_parts = this_part + 1; + alloc_size = *num_parts * sizeof(struct mtd_partition) + + extra_mem_size; + parts = kmalloc(alloc_size, GFP_KERNEL); + if (!parts) + { + printk(KERN_ERR ERRP "out of memory\n"); + return 0; + } + memset(parts, 0, alloc_size); + extra_mem = (unsigned char *)(parts + *num_parts); + } + /* enter this partition (offset will be calculated later if it is zero at this point) */ + parts[this_part].size = size; + parts[this_part].offset = offset; + parts[this_part].mask_flags = mask_flags; + if (name) + { + strncpy(extra_mem, name, name_len); + extra_mem[name_len] = 0; + } + else + { + sprintf(extra_mem, "Partition_%03d", this_part); + } + parts[this_part].name = extra_mem; + extra_mem += name_len + 1; + + dbg(("partition %d: name <%s>, offset %x, size %x, mask flags %x\n", + this_part, + parts[this_part].name, + parts[this_part].offset, + parts[this_part].size, + parts[this_part].mask_flags)); + + /* return (updated) pointer to extra_mem memory */ + if (extra_mem_ptr) + *extra_mem_ptr = extra_mem; + + /* return (updated) pointer command line string */ + *retptr = s; + + /* return partition table */ + return parts; +} + +/* + * Parse the command line. + */ +static int mtdpart_setup_real(char *s) +{ + cmdline_parsed = 1; + + for( ; s != NULL; ) + { + struct cmdline_mtd_partition *this_mtd; + struct mtd_partition *parts; + int mtd_id_len; + int num_parts; + char *p, *mtd_id; + + mtd_id = s; + /* fetch */ + if (!(p = strchr(s, ':'))) + { + printk(KERN_ERR ERRP "no mtd-id\n"); + return 0; + } + mtd_id_len = p - mtd_id; + + dbg(("parsing <%s>\n", p+1)); + + /* + * parse one mtd. have it reserve memory for the + * struct cmdline_mtd_partition and the mtd-id string. + */ + parts = newpart(p + 1, /* cmdline */ + &s, /* out: updated cmdline ptr */ + &num_parts, /* out: number of parts */ + 0, /* first partition */ + (unsigned char**)&this_mtd, /* out: extra mem */ + mtd_id_len + 1 + sizeof(*this_mtd)); + + /* enter results */ + this_mtd->parts = parts; + this_mtd->num_parts = num_parts; + this_mtd->mtd_id = (char*)(this_mtd + 1); + strncpy(this_mtd->mtd_id, mtd_id, mtd_id_len); + this_mtd->mtd_id[mtd_id_len] = 0; + + /* link into chain */ + this_mtd->next = partitions; + partitions = this_mtd; + + dbg(("mtdid=<%s> num_parts=<%d>\n", + this_mtd->mtd_id, this_mtd->num_parts)); + + + /* EOS - we're done */ + if (*s == 0) + break; + + /* does another spec follow? */ + if (*s != ';') + { + printk(KERN_ERR ERRP "bad character after partition (%c)\n", *s); + return 0; + } + s++; + } + return 1; +} + +/* + * Main function to be called from the MTD mapping driver/device to + * obtain the partitioning information. At this point the command line + * arguments will actually be parsed and turned to struct mtd_partition + * information. + */ +int parse_cmdline_partitions(struct mtd_info *master, + struct mtd_partition **pparts, + const char *mtd_id) +{ + unsigned long offset; + int i; + struct cmdline_mtd_partition *part; + + if (!cmdline) + return -EINVAL; + + /* parse command line */ + if (!cmdline_parsed) + mtdpart_setup_real(cmdline); + + for(part = partitions; part; part = part->next) + { + if (!strcmp(part->mtd_id, mtd_id)) + { + for(i = 0, offset = 0; i < part->num_parts; i++) + { + if (!part->parts[i].offset) + part->parts[i].offset = offset; + else + offset = part->parts[i].offset; + if (part->parts[i].size == SIZE_REMAINING) + part->parts[i].size = master->size - offset; + if (offset + part->parts[i].size > master->size) + { + printk(KERN_WARNING ERRP + "%s: partitioning exceeds flash size, truncating\n", + mtd_id); + part->parts[i].size = master->size - offset; + part->num_parts = i; + } + offset += part->parts[i].size; + } + *pparts = part->parts; + return part->num_parts; + } + } + return -EINVAL; +} + + +/* + * This is the handler for our kernel parameter, called from + * main.c::checksetup(). Note that we can not yet kmalloc() anything, + * so we only save the commandline for later processing. + */ +static int __init mtdpart_setup(char *s) +{ + cmdline = s; + return 1; +} + +__setup("mtdparts=", mtdpart_setup); + +EXPORT_SYMBOL(parse_cmdline_partitions); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marius Groeger "); +MODULE_DESCRIPTION("Command line configuration of MTD partitions"); diff --git a/drivers/mtd/maps/Config.help b/drivers/mtd/maps/Config.help index aaf3a1aa894e..d4cc1af6505e 100644 --- a/drivers/mtd/maps/Config.help +++ b/drivers/mtd/maps/Config.help @@ -1,3 +1,32 @@ +CONFIG_MTD_CDB89712 + This enables access to the flash or ROM chips on the CDB89712 board. + If you have such a board, say 'Y'. + +CONFIG_MTD_CEIVA + This enables access to the flash chips on the Ceiva/Polaroid + PhotoMax Digital Picture Frame. + If you have such a device, say 'Y'. + +CONFIG_MTD_FORTUNET + This enables access to the Flash on the FortuNet board. If you + have such a board, say 'Y'. + +CONFIG_MTD_AUTCPU12 + This enables access to the NV-RAM on autronix autcpu12 board. + If you have such a board, say 'Y'. + +CONFIG_MTD_EDB7312 + This enables access to the CFI Flash on the Cogent EDB7312 board. + If you have such a board, say 'Y' here. + +CONFIG_MTD_NAND_EDB7312 + This enables access to the NAND Flash on the Cogent EDB7312 board. + If you have such a board, say 'Y' here. + +CONFIG_MTD_IMPA7 + This enables access to the NOR Flash on the impA7 board of + implementa GmbH. If you have such a board, say 'Y' here. + CONFIG_MTD_SA1100 This enables access to the flash chips on most platforms based on the SA1100 and SA1110, including the Assabet and the Compaq iPAQ. @@ -39,6 +68,12 @@ CONFIG_MTD_SUN_UFLASH CONFIG_MTD_NORA If you had to ask, you don't have one. Say 'N'. +CONFIG_MTD_L440GX + Support for treating the BIOS flash chip on Intel L440GX motherboards + as an MTD device - with this you can reprogram your BIOS. + + BE VERY CAREFUL. + CONFIG_MTD_PNC2000 PNC-2000 is the name of Network Camera product from PHOTRON Ltd. in Japan. It uses CFI-compliant flash. @@ -50,6 +85,13 @@ CONFIG_MTD_RPXLITE to communicate with the chips on the RPXLite board. More at . +CONFIG_MTD_TQM8XXL + The TQM8xxL PowerPC board has up to two banks of CFI-compliant + chips, currently uses AMD one. This 'mapping' driver supports + that arrangement, allowing the CFI probe and command set driver + code to communicate with the chips on the TQM8xxL board. More at + . + CONFIG_MTD_SC520CDP The SC520 CDP board has two banks of CFI-compliant chips and one Dual-in-line JEDEC chip. This 'mapping' driver supports that @@ -59,7 +101,7 @@ CONFIG_MTD_SBC_GXX This provides a driver for the on-board flash of Arcom Control Systems' SBC-GXn family of boards, formerly known as SBC-MediaGX. By default the flash is split into 3 partitions which are accessed - as separate MTD devices. This board utilizes Intel StrataFlash. + as separate MTD devices. This board utilizes Intel StrataFlash. More info at . @@ -78,6 +120,11 @@ CONFIG_MTD_NETSC520 demonstration board. If you have one of these boards and would like to use the flash chips on it, say 'Y'. +CONFIG_MTD_OCELOT + This enables access routines for the boot flash device and for the + NVRAM on the Momenco Ocelot board. If you have one of these boards + and would like access to either of these, say 'Y'. + CONFIG_MTD_ELAN_104NC This provides a driver for the on-board flash of the Arcom Control System's ELAN-104NC development board. By default the flash @@ -91,17 +138,17 @@ CONFIG_MTD_DC21285 . CONFIG_MTD_CSTM_MIPS_IXX - This provides a mapping driver for the Integrated Tecnology Express, - Inc (ITE) QED-4N-S01B eval board and the Globespan IVR Reference - Board. It provides the necessary addressing, length, buswidth, vpp - code and addition setup of the flash device for these boards. In - addition, this mapping driver can be used for other boards via - setting of the CONFIG_MTD_CSTM_MIPS_IXX_START/LEN/BUSWIDTH - parameters. This mapping will provide one mtd device using one - partition. The start address can be offset from the beginning of - flash and the len can be less than the total flash device size to - allow a window into the flash. Both CFI and JEDEC probes are - called. + This provides a mapping driver for the Integrated Tecnology + Express, Inc (ITE) QED-4N-S01B eval board and the Globespan IVR + Reference Board. It provides the necessary addressing, length, + buswidth, vpp code and addition setup of the flash device for + these boards. In addition, this mapping driver can be used for + other boards via setting of the CONFIG_MTD_CSTM_MIPS_IXX_START/ + LEN/BUSWIDTH parameters. This mapping will provide one mtd device + using one partition. The start address can be offset from the + beginning of flash and the len can be less than the total flash + device size to allow a window into the flash. Both CFI and JEDEC + probes are called. CONFIG_MTD_CSTM_MIPS_IXX_START This is the physical memory location that the MTD driver will @@ -141,6 +188,11 @@ CONFIG_MTD_OCTAGON Computer. More information on the board is available at . +CONFIG_MTD_PCMCIA + Map driver for accessing PCMCIA linear flash memory cards. These + cards are usually around 4-16MiB in size. This does not include + Compact Flash cards which are treated as IDE devices. + CONFIG_MTD_VMAX This provides a 'mapping' driver which supports the way in which the flash chips are connected in the Tempustech VMAX SBC301 Single @@ -148,32 +200,21 @@ CONFIG_MTD_VMAX . CONFIG_MTD_CFI_FLAGADM - Mapping for the Flaga digital module. If you don´t have one, ignore + Mapping for the Flaga digital module. If you don´t have one, ignore this setting. -CONFIG_MTD_OCELOT - This enables access routines for the boot flash device and for the - NVRAM on the Momenco Ocelot board. If you have one of these boards - and would like access to either of these, say 'Y'. - -CONFIG_MTD_CDB89712 - This enables access to the flash or ROM chips on the CDB89712 board. - If you have such a board, say 'Y'. - -CONFIG_MTD_L440GX - Support for treating the BIOS flash chip on Intel L440GX motherboards - as an MTD device - with this you can reprogram your BIOS. - - BE VERY CAREFUL. - CONFIG_MTD_SOLUTIONENGINE This enables access to the flash chips on the Hitachi SolutionEngine and similar boards. Say 'Y' if you are building a kernel for such a board. -CONFIG_MTD_TQM8XXL - The TQM8xxL PowerPC board has up to two banks of CFI-compliant - chips, currently uses AMD one. This 'mapping' driver supports - that arrangement, allowing the CFI probe and command set driver - code to communicate with the chips on the TQM8xxL board. More at - . +CONFIG_MTD_EPXA10DB + This enables support for the flash devices on the Altera + Excalibur XA10 Development Board. If you are building a kernel + for on of these boards then you should say 'Y' otherwise say 'N'. + +CONFIG_MTD_PCI + Mapping for accessing flash devices on add-in cards like the Intel XScale + IQ80310 card, and the Intel EBSA285 card in blank ROM programming mode + (please see the manual for the link settings). + If you are not sure, say N. diff --git a/drivers/mtd/maps/Config.in b/drivers/mtd/maps/Config.in index 7b4cbd4eda85..e0668372fa79 100644 --- a/drivers/mtd/maps/Config.in +++ b/drivers/mtd/maps/Config.in @@ -56,8 +56,18 @@ if [ "$CONFIG_ARM" = "y" ]; then dep_tristate ' CFI Flash device mapped on ARM Integrator/P720T' CONFIG_MTD_ARM_INTEGRATOR $CONFIG_MTD_CFI dep_tristate ' Cirrus CDB89712 evaluation board mappings' CONFIG_MTD_CDB89712 $CONFIG_MTD_CFI $CONFIG_ARCH_CDB89712 dep_tristate ' CFI Flash device mapped on StrongARM SA11x0' CONFIG_MTD_SA1100 $CONFIG_MTD_CFI $CONFIG_ARCH_SA1100 $CONFIG_MTD_PARTITIONS - dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE $CONFIG_MTD_PARTITIONS + dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE dep_tristate ' CFI Flash device mapped on the XScale IQ80310 board' CONFIG_MTD_IQ80310 $CONFIG_MTD_CFI $CONFIG_ARCH_IQ80310 + dep_tristate ' CFI Flash device mapped on Epxa10db' CONFIG_MTD_EPXA10DB $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_CAMELOT + dep_tristate ' CFI Flash device mapped on the FortuNet board' CONFIG_MTD_FORTUNET $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_SA1100_FORTUNET + dep_tristate ' NV-RAM mapping AUTCPU12 board' CONFIG_MTD_AUTCPU12 $CONFIG_ARCH_AUTCPU12 + dep_tristate ' CFI Flash device mapped on EDB7312' CONFIG_MTD_EDB7312 $CONFIG_MTD_CFI + dep_tristate ' JEDEC Flash device mapped on impA7' CONFIG_MTD_IMPA7 $CONFIG_MTD_JEDECPROBE + dep_tristate ' JEDEC Flash device mapped on Ceiva/Polaroid PhotoMax Digital Picture Frame' CONFIG_MTD_CEIVA $CONFIG_MTD_JEDECPROBE $CONFIG_ARCH_CEIVA fi +# This needs CFI or JEDEC, depending on the cards found. +dep_tristate ' PCI MTD driver' CONFIG_MTD_PCI $CONFIG_MTD $CONFIG_PCI +dep_tristate ' PCMCIA MTD driver' CONFIG_MTD_PCMCIA $CONFIG_MTD $CONFIG_PCMCIA + endmenu diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index c0bdc2fa8f23..f4acee989d04 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -4,29 +4,37 @@ # $Id: Makefile,v 1.13 2001/08/16 15:16:58 rmk Exp $ # Chip mappings -obj-$(CONFIG_MTD_CDB89712) += cdb89712.o +obj-$(CONFIG_MTD_CDB89712) += cdb89712.o obj-$(CONFIG_MTD_ARM_INTEGRATOR)+= integrator-flash.o obj-$(CONFIG_MTD_CFI_FLAGADM) += cfi_flagadm.o -obj-$(CONFIG_MTD_CSTM_MIPS_IXX) += cstm_mips_ixx.o -obj-$(CONFIG_MTD_DC21285) += dc21285.o -obj-$(CONFIG_MTD_ELAN_104NC) += elan-104nc.o +obj-$(CONFIG_MTD_CSTM_MIPS_IXX) += cstm_mips_ixx.o +obj-$(CONFIG_MTD_DC21285) += dc21285.o +obj-$(CONFIG_MTD_ELAN_104NC) += elan-104nc.o +obj-$(CONFIG_MTD_EPXA10DB) += epxa10db-flash.o obj-$(CONFIG_MTD_IQ80310) += iq80310.o obj-$(CONFIG_MTD_L440GX) += l440gx.o obj-$(CONFIG_MTD_NORA) += nora.o +obj-$(CONFIG_MTD_CEIVA) += ceiva.o obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o obj-$(CONFIG_MTD_PNC2000) += pnc2000.o +obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o -obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o +obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o obj-$(CONFIG_MTD_NETSC520) += netsc520.o -obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o +obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o obj-$(CONFIG_MTD_VMAX) += vmax301.o obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o obj-$(CONFIG_MTD_OCELOT) += ocelot.o obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o +obj-$(CONFIG_MTD_PCI) += pci.o +obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o +obj-$(CONFIG_MTD_EDB7312) += edb7312.o +obj-$(CONFIG_MTD_IMPA7) += impa7.o +obj-$(CONFIG_MTD_FORTUNET) += fortunet.o include $(TOPDIR)/Rules.make diff --git a/drivers/mtd/maps/autcpu12-nvram.c b/drivers/mtd/maps/autcpu12-nvram.c new file mode 100644 index 000000000000..db78b01e6438 --- /dev/null +++ b/drivers/mtd/maps/autcpu12-nvram.c @@ -0,0 +1,179 @@ +/* + * NV-RAM memory access on autcpu12 + * (C) 2002 Thomas Gleixner (gleixner@autronix.de) + * + * $Id: autcpu12-nvram.c,v 1.1 2002/02/22 09:30:24 gleixner Exp $ + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +__u8 autcpu12_read8(struct map_info *map, unsigned long ofs) +{ + return __raw_readb(map->map_priv_1 + ofs); +} + +__u16 autcpu12_read16(struct map_info *map, unsigned long ofs) +{ + return __raw_readw(map->map_priv_1 + ofs); +} + +__u32 autcpu12_read32(struct map_info *map, unsigned long ofs) +{ + return __raw_readl(map->map_priv_1 + ofs); +} + +void autcpu12_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + __raw_writeb(d, map->map_priv_1 + adr); + mb(); +} + +void autcpu12_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + __raw_writew(d, map->map_priv_1 + adr); + mb(); +} + +void autcpu12_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + __raw_writel(d, map->map_priv_1 + adr); + mb(); +} + +void autcpu12_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, map->map_priv_1 + from, len); +} + +void autcpu12_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + while(len) { + __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to); + from++; + to++; + len--; + } +} + +static struct mtd_info *sram_mtd; + +struct map_info autcpu12_sram_map = { + name: "SRAM", + size: 32768, + buswidth: 8, + read8: autcpu12_read8, + read16: autcpu12_read16, + read32: autcpu12_read32, + copy_from: autcpu12_copy_from, + write8: autcpu12_write8, + write16: autcpu12_write16, + write32: autcpu12_write32, + copy_to: autcpu12_copy_to +}; + +static int __init init_autcpu12_sram (void) +{ + int err, save0, save1; + + autcpu12_sram_map.map_priv_1 = (unsigned long)ioremap(0x12000000, SZ_128K); + if (!autcpu12_sram_map.map_priv_1) { + printk("Failed to ioremap autcpu12 NV-RAM space\n"); + err = -EIO; + goto out; + } + + /* + * Check for 32K/128K + * read ofs 0 + * read ofs 0x10000 + * Write complement to ofs 0x100000 + * Read and check result on ofs 0x0 + * Restore contents + */ + save0 = autcpu12_read32(&autcpu12_sram_map,0); + save1 = autcpu12_read32(&autcpu12_sram_map,0x10000); + autcpu12_write32(&autcpu12_sram_map,~save0,0x10000); + /* if we find this pattern on 0x0, we have 32K size + * restore contents and exit + */ + if ( autcpu12_read32(&autcpu12_sram_map,0) != save0) { + autcpu12_write32(&autcpu12_sram_map,save0,0x0); + goto map; + } + /* We have a 128K found, restore 0x10000 and set size + * to 128K + */ + autcpu12_write32(&autcpu12_sram_map,save1,0x10000); + autcpu12_sram_map.size = SZ_128K; + +map: + sram_mtd = do_map_probe("map_ram", &autcpu12_sram_map); + if (!sram_mtd) { + printk("NV-RAM probe failed\n"); + err = -ENXIO; + goto out_ioremap; + } + + sram_mtd->module = THIS_MODULE; + sram_mtd->erasesize = 16; + + if (add_mtd_device(sram_mtd)) { + printk("NV-RAM device addition failed\n"); + err = -ENOMEM; + goto out_probe; + } + + printk("NV-RAM device size %ldK registered on AUTCPU12\n",autcpu12_sram_map.size/SZ_1K); + + return 0; + +out_probe: + map_destroy(sram_mtd); + sram_mtd = 0; + +out_ioremap: + iounmap((void *)autcpu12_sram_map.map_priv_1); +out: + return err; +} + +static void __exit cleanup_autcpu12_maps(void) +{ + if (sram_mtd) { + del_mtd_device(sram_mtd); + map_destroy(sram_mtd); + iounmap((void *)autcpu12_sram_map.map_priv_1); + } +} + +module_init(init_autcpu12_sram); +module_exit(cleanup_autcpu12_maps); + +MODULE_AUTHOR("Thomas Gleixner"); +MODULE_DESCRIPTION("autcpu12 NV-RAM map driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/ceiva.c b/drivers/mtd/maps/ceiva.c new file mode 100644 index 000000000000..259a9a8b76c0 --- /dev/null +++ b/drivers/mtd/maps/ceiva.c @@ -0,0 +1,408 @@ +/* + * Ceiva flash memory driver. + * Copyright (C) 2002 Rob Scott + * + * Note: this driver supports jedec compatible devices. Modification + * for CFI compatible devices should be straight forward: change + * jedec_probe to cfi_probe. + * + * Based on: sa1100-flash.c, which has the following copyright: + * Flash memory access on SA11x0 based devices + * + * (C) 2000 Nicolas Pitre + * + * $Id: ceiva.c,v 1.2 2002/10/14 12:50:22 rmk Exp $ + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * This isnt complete yet, so... + */ +#define CONFIG_MTD_CEIVA_STATICMAP + +static __u8 clps_read8(struct map_info *map, unsigned long ofs) +{ + return readb(map->map_priv_1 + ofs); +} + +static __u16 clps_read16(struct map_info *map, unsigned long ofs) +{ + return readw(map->map_priv_1 + ofs); +} + +static __u32 clps_read32(struct map_info *map, unsigned long ofs) +{ + return readl(map->map_priv_1 + ofs); +} + +static void clps_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy(to, (void *)(map->map_priv_1 + from), len); +} + +static void clps_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + writeb(d, map->map_priv_1 + adr); +} + +static void clps_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + writew(d, map->map_priv_1 + adr); +} + +static void clps_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + writel(d, map->map_priv_1 + adr); +} + +static void clps_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy((void *)(map->map_priv_1 + to), from, len); +} + +static struct map_info clps_map __initdata = { + name: "clps flash", + read8: clps_read8, + read16: clps_read16, + read32: clps_read32, + copy_from: clps_copy_from, + write8: clps_write8, + write16: clps_write16, + write32: clps_write32, + copy_to: clps_copy_to, +}; + +#ifdef CONFIG_MTD_CEIVA_STATICMAP +/* + * See include/linux/mtd/partitions.h for definition of the mtd_partition + * structure. + * + * Please note: + * 1. The flash size given should be the largest flash size that can + * be accomodated. + * + * 2. The bus width must defined in clps_setup_flash. + * + * The MTD layer will detect flash chip aliasing and reduce the size of + * the map accordingly. + * + */ + +#ifdef CONFIG_ARCH_CEIVA +/* Flash / Partition sizing */ +/* For the 28F8003, we use the block mapping to calcuate the sizes */ +#define MAX_SIZE_KiB (16 + 8 + 8 + 96 + (7*128)) +#define BOOT_PARTITION_SIZE_KiB (16) +#define PARAMS_PARTITION_SIZE_KiB (8) +#define KERNEL_PARTITION_SIZE_KiB (4*128) +/* Use both remaing portion of first flash, and all of second flash */ +#define ROOT_PARTITION_SIZE_KiB (3*128) + (8*128) + +static struct mtd_partition ceiva_partitions[] = { + { + name: "Ceiva BOOT partition", + size: BOOT_PARTITION_SIZE_KiB*1024, + offset: 0, + + },{ + name: "Ceiva parameters partition", + size: PARAMS_PARTITION_SIZE_KiB*1024, + offset: (16 + 8) * 1024, + },{ + name: "Ceiva kernel partition", + size: (KERNEL_PARTITION_SIZE_KiB)*1024, + offset: 0x20000, + + },{ + name: "Ceiva root filesystem partition", + offset: MTDPART_OFS_APPEND, + size: (ROOT_PARTITION_SIZE_KiB)*1024, + } +}; +#endif + +static int __init clps_static_partitions(struct mtd_partition **parts) +{ + int nb_parts = 0; + +#ifdef CONFIG_ARCH_CEIVA + if (machine_is_ceiva()) { + *parts = ceiva_partitions; + nb_parts = ARRAY_SIZE(ceiva_partitions); + } +#endif + return nb_parts; +} +#endif + +struct clps_info { + unsigned long base; + unsigned long size; + int width; + void *vbase; + struct map_info *map; + struct mtd_info *mtd; + struct resource *res; +}; + +#define NR_SUBMTD 4 + +static struct clps_info info[NR_SUBMTD]; + +static int __init clps_setup_mtd(struct clps_info *clps, int nr, struct mtd_info **rmtd) +{ + struct mtd_info *subdev[nr]; + struct map_info *maps; + int i, found = 0, ret = 0; + + /* + * Allocate the map_info structs in one go. + */ + maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL); + if (!maps) + return -ENOMEM; + + /* + * Claim and then map the memory regions. + */ + for (i = 0; i < nr; i++) { + if (clps[i].base == (unsigned long)-1) + break; + + clps[i].res = request_mem_region(clps[i].base, clps[i].size, "clps flash"); + if (!clps[i].res) { + ret = -EBUSY; + break; + } + + clps[i].map = maps + i; + memcpy(clps[i].map, &clps_map, sizeof(struct map_info)); + + clps[i].vbase = ioremap(clps[i].base, clps[i].size); + if (!clps[i].vbase) { + ret = -ENOMEM; + break; + } + + clps[i].map->map_priv_1 = (unsigned long)clps[i].vbase; + clps[i].map->buswidth = clps[i].width; + clps[i].map->size = clps[i].size; + + clps[i].mtd = do_map_probe("jedec_probe", clps[i].map); + if (clps[i].mtd == NULL) { + ret = -ENXIO; + break; + } + clps[i].mtd->module = THIS_MODULE; + subdev[i] = clps[i].mtd; + + printk(KERN_INFO "clps flash: JEDEC device at 0x%08lx, %dMiB, " + "%d-bit\n", clps[i].base, clps[i].mtd->size >> 20, + clps[i].width * 8); + found += 1; + } + + /* + * ENXIO is special. It means we didn't find a chip when + * we probed. We need to tear down the mapping, free the + * resource and mark it as such. + */ + if (ret == -ENXIO) { + iounmap(clps[i].vbase); + clps[i].vbase = NULL; + release_resource(clps[i].res); + clps[i].res = NULL; + } + + /* + * If we found one device, don't bother with concat support. + * If we found multiple devices, use concat if we have it + * available, otherwise fail. + */ + if (ret == 0 || ret == -ENXIO) { + if (found == 1) { + *rmtd = subdev[0]; + ret = 0; + } else if (found > 1) { + /* + * We detected multiple devices. Concatenate + * them together. + */ +#ifdef CONFIG_MTD_CONCAT + *rmtd = mtd_concat_create(subdev, found, + "clps flash"); + if (*rmtd == NULL) + ret = -ENXIO; +#else + printk(KERN_ERR "clps flash: multiple devices " + "found but MTD concat support disabled.\n"); + ret = -ENXIO; +#endif + } + } + + /* + * If we failed, clean up. + */ + if (ret) { + do { + if (clps[i].mtd) + map_destroy(clps[i].mtd); + if (clps[i].vbase) + iounmap(clps[i].vbase); + if (clps[i].res) + release_resource(clps[i].res); + } while (i--); + + kfree(maps); + } + + return ret; +} + +static void __exit clps_destroy_mtd(struct clps_info *clps, struct mtd_info *mtd) +{ + int i; + + del_mtd_partitions(mtd); + + if (mtd != clps[0].mtd) + mtd_concat_destroy(mtd); + + for (i = NR_SUBMTD; i >= 0; i--) { + if (clps[i].mtd) + map_destroy(clps[i].mtd); + if (clps[i].vbase) + iounmap(clps[i].vbase); + if (clps[i].res) + release_resource(clps[i].res); + } + kfree(clps[0].map); +} + +/* + * We define the memory space, size, and width for the flash memory + * space here. + */ + +static int __init clps_setup_flash(void) +{ + int nr; + +#ifdef CONFIG_ARCH_CEIVA + if (machine_is_ceiva()) { + info[0].base = CS0_PHYS_BASE; + info[0].size = SZ_32M; + info[0].width = CEIVA_FLASH_WIDTH; + info[1].base = CS1_PHYS_BASE; + info[1].size = SZ_32M; + info[1].width = CEIVA_FLASH_WIDTH; + nr = 2; + } +#endif + return nr; +} + +extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *); + +static struct mtd_partition *parsed_parts; + +static void __init clps_locate_partitions(struct mtd_info *mtd) +{ + const char *part_type = NULL; + int nr_parts = 0; + do { + /* + * Partition selection stuff. + */ +#ifdef CONFIG_MTD_CMDLINE_PARTS + nr_parts = parse_cmdline_partitions(mtd, &parsed_parts, "clps"); + if (nr_parts > 0) { + part_type = "command line"; + break; + } +#endif +#ifdef CONFIG_MTD_REDBOOT_PARTS + nr_parts = parse_redboot_partitions(mtd, &parsed_parts); + if (nr_parts > 0) { + part_type = "RedBoot"; + break; + } +#endif +#ifdef CONFIG_MTD_CEIVA_STATICMAP + nr_parts = clps_static_partitions(&parsed_parts); + if (nr_parts > 0) { + part_type = "static"; + break; + } + printk("found: %d partitions\n", nr_parts); +#endif + } while (0); + + if (nr_parts == 0) { + printk(KERN_NOTICE "clps flash: no partition info " + "available, registering whole flash\n"); + add_mtd_device(mtd); + } else { + printk(KERN_NOTICE "clps flash: using %s partition " + "definition\n", part_type); + add_mtd_partitions(mtd, parsed_parts, nr_parts); + } + + /* Always succeeds. */ +} + +static void __exit clps_destroy_partitions(void) +{ + if (parsed_parts) + kfree(parsed_parts); +} + +static struct mtd_info *mymtd; + +static int __init clps_mtd_init(void) +{ + int ret; + int nr; + + nr = clps_setup_flash(); + if (nr < 0) + return nr; + + ret = clps_setup_mtd(info, nr, &mymtd); + if (ret) + return ret; + + clps_locate_partitions(mymtd); + + return 0; +} + +static void __exit clps_mtd_cleanup(void) +{ + clps_destroy_mtd(info, mymtd); + clps_destroy_partitions(); +} + +module_init(clps_mtd_init); +module_exit(clps_mtd_cleanup); + +MODULE_AUTHOR("Rob Scott"); +MODULE_DESCRIPTION("Cirrus Logic JEDEC map driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/dc21285.c b/drivers/mtd/maps/dc21285.c index e7eea7ef53b2..f030f3447302 100644 --- a/drivers/mtd/maps/dc21285.c +++ b/drivers/mtd/maps/dc21285.c @@ -5,9 +5,9 @@ * * This code is GPL * - * $Id: dc21285.c,v 1.6 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: dc21285.c,v 1.9 2002/10/14 12:22:10 rmk Exp $ */ - +#include #include #include #include @@ -44,15 +44,15 @@ void dc21285_copy_from(struct map_info *map, void *to, unsigned long from, ssize void dc21285_write8(struct map_info *map, __u8 d, unsigned long adr) { - *CSR_ROMWRITEREG = adr; + *CSR_ROMWRITEREG = adr & 3; adr &= ~3; *(__u8*)(map->map_priv_1 + adr) = d; } void dc21285_write16(struct map_info *map, __u16 d, unsigned long adr) { - *CSR_ROMWRITEREG = adr; - adr &= ~1; + *CSR_ROMWRITEREG = adr & 3; + adr &= ~3; *(__u16*)(map->map_priv_1 + adr) = d; } @@ -131,7 +131,7 @@ int __init init_dc21285(void) dc21285_map.buswidth*8); /* Let's map the flash area */ - dc21285_map.map_priv_1 = (unsigned long)__ioremap(DC21285_FLASH, 16*1024*1024, 0); + dc21285_map.map_priv_1 = (unsigned long)ioremap(DC21285_FLASH, 16*1024*1024); if (!dc21285_map.map_priv_1) { printk("Failed to ioremap\n"); return -EIO; @@ -139,21 +139,22 @@ int __init init_dc21285(void) mymtd = do_map_probe("cfi_probe", &dc21285_map); if (mymtd) { - int nrparts; + int nrparts = 0; mymtd->module = THIS_MODULE; /* partition fixup */ +#ifdef CONFIG_MTD_REDBOOT_PARTS nrparts = parse_redboot_partitions(mymtd, &dc21285_parts); - if (nrparts <=0) { +#endif + if (nrparts > 0) { + add_mtd_partitions(mymtd, dc21285_parts, nrparts); + } else if (nrparts == 0) { printk(KERN_NOTICE "RedBoot partition table failed\n"); - iounmap((void *)dc21285_map.map_priv_1); - return -ENXIO; + add_mtd_device(mymtd); } - add_mtd_partitions(mymtd, dc21285_parts, nrparts); - /* * Flash timing is determined with bits 19-16 of the * CSR_SA110_CNTL. The value is the number of wait cycles, or diff --git a/drivers/mtd/maps/edb7312.c b/drivers/mtd/maps/edb7312.c new file mode 100644 index 000000000000..405429d92735 --- /dev/null +++ b/drivers/mtd/maps/edb7312.c @@ -0,0 +1,202 @@ +/* + * $Id: edb7312.c,v 1.2 2002/09/05 05:11:24 acurtis Exp $ + * + * Handle mapping of the NOR flash on Cogent EDB7312 boards + * + * Copyright 2002 SYSGO Real-Time Solutions GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MTD_PARTITIONS +#include +#endif + +#define WINDOW_ADDR 0x00000000 /* physical properties of flash */ +#define WINDOW_SIZE 0x01000000 +#define BUSWIDTH 2 +#define FLASH_BLOCKSIZE_MAIN 0x20000 +#define FLASH_NUMBLOCKS_MAIN 128 +/* can be "cfi_probe", "jedec_probe", "map_rom", 0 }; */ +#define PROBETYPES { "cfi_probe", 0 } + +#define MSG_PREFIX "EDB7312-NOR:" /* prefix for our printk()'s */ +#define MTDID "edb7312-nor" /* for mtdparts= partitioning */ + +static struct mtd_info *mymtd; + +__u8 edb7312nor_read8(struct map_info *map, unsigned long ofs) +{ + return __raw_readb(map->map_priv_1 + ofs); +} + +__u16 edb7312nor_read16(struct map_info *map, unsigned long ofs) +{ + return __raw_readw(map->map_priv_1 + ofs); +} + +__u32 edb7312nor_read32(struct map_info *map, unsigned long ofs) +{ + return __raw_readl(map->map_priv_1 + ofs); +} + +void edb7312nor_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, map->map_priv_1 + from, len); +} + +void edb7312nor_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + __raw_writeb(d, map->map_priv_1 + adr); + mb(); +} + +void edb7312nor_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + __raw_writew(d, map->map_priv_1 + adr); + mb(); +} + +void edb7312nor_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + __raw_writel(d, map->map_priv_1 + adr); + mb(); +} + +void edb7312nor_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy_toio(map->map_priv_1 + to, from, len); +} + +struct map_info edb7312nor_map = { + name: "NOR flash on EDB7312", + size: WINDOW_SIZE, + buswidth: BUSWIDTH, + read8: edb7312nor_read8, + read16: edb7312nor_read16, + read32: edb7312nor_read32, + copy_from: edb7312nor_copy_from, + write8: edb7312nor_write8, + write16: edb7312nor_write16, + write32: edb7312nor_write32, + copy_to: edb7312nor_copy_to +}; + +#ifdef CONFIG_MTD_PARTITIONS + +/* + * MTD partitioning stuff + */ +static struct mtd_partition static_partitions[3] = +{ + { + name: "ARMboot", + size: 0x40000, + offset: 0 + }, + { + name: "Kernel", + size: 0x200000, + offset: 0x40000 + }, + { + name: "RootFS", + size: 0xDC0000, + offset: 0x240000 + }, +}; + +#define NB_OF(x) (sizeof (x) / sizeof (x[0])) + +#ifdef CONFIG_MTD_CMDLINE_PARTS +int parse_cmdline_partitions(struct mtd_info *master, + struct mtd_partition **pparts, + const char *mtd_id); +#endif + +#endif + +static int mtd_parts_nb = 0; +static struct mtd_partition *mtd_parts = 0; + +int __init init_edb7312nor(void) +{ + static const char *rom_probe_types[] = PROBETYPES; + const char **type; + const char *part_type = 0; + + printk(KERN_NOTICE MSG_PREFIX "0x%08x at 0x%08x\n", + WINDOW_SIZE, WINDOW_ADDR); + edb7312nor_map.map_priv_1 = (unsigned long) + ioremap(WINDOW_ADDR, WINDOW_SIZE); + + if (!edb7312nor_map.map_priv_1) { + printk(MSG_PREFIX "failed to ioremap\n"); + return -EIO; + } + + mymtd = 0; + type = rom_probe_types; + for(; !mymtd && *type; type++) { + mymtd = do_map_probe(*type, &edb7312nor_map); + } + if (mymtd) { + mymtd->module = THIS_MODULE; + +#ifdef CONFIG_MTD_PARTITIONS +#ifdef CONFIG_MTD_CMDLINE_PARTS + mtd_parts_nb = parse_cmdline_partitions(mymtd, &mtd_parts, MTDID); + if (mtd_parts_nb > 0) + part_type = "command line"; +#endif + if (mtd_parts_nb == 0) + { + mtd_parts = static_partitions; + mtd_parts_nb = NB_OF(static_partitions); + part_type = "static"; + } +#endif + add_mtd_device(mymtd); + if (mtd_parts_nb == 0) + printk(KERN_NOTICE MSG_PREFIX "no partition info available\n"); + else + { + printk(KERN_NOTICE MSG_PREFIX + "using %s partition definition\n", part_type); + add_mtd_partitions(mymtd, mtd_parts, mtd_parts_nb); + } + return 0; + } + + iounmap((void *)edb7312nor_map.map_priv_1); + return -ENXIO; +} + +static void __exit cleanup_edb7312nor(void) +{ + if (mymtd) { + del_mtd_device(mymtd); + map_destroy(mymtd); + } + if (edb7312nor_map.map_priv_1) { + iounmap((void *)edb7312nor_map.map_priv_1); + edb7312nor_map.map_priv_1 = 0; + } +} + +module_init(init_edb7312nor); +module_exit(cleanup_edb7312nor); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marius Groeger "); +MODULE_DESCRIPTION("Generic configurable MTD map driver"); diff --git a/drivers/mtd/maps/epxa10db-flash.c b/drivers/mtd/maps/epxa10db-flash.c new file mode 100644 index 000000000000..cb4c76e4bb71 --- /dev/null +++ b/drivers/mtd/maps/epxa10db-flash.c @@ -0,0 +1,233 @@ +/* + * Flash memory access on EPXA based devices + * + * (C) 2000 Nicolas Pitre + * Copyright (C) 2001 Altera Corporation + * Copyright (C) 2001 Red Hat, Inc. + * + * $Id: epxa10db-flash.c,v 1.4 2002/08/22 10:46:19 cdavies Exp $ + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef CONFIG_EPXA10DB +#define BOARD_NAME "EPXA10DB" +#else +#define BOARD_NAME "EPXA1DB" +#endif + +static int nr_parts = 0; +static struct mtd_partition *parts; + +static struct mtd_info *mymtd; + +extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **); +static int epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts); + +static __u8 epxa_read8(struct map_info *map, unsigned long ofs) +{ + return __raw_readb(map->map_priv_1 + ofs); +} + +static __u16 epxa_read16(struct map_info *map, unsigned long ofs) +{ + return __raw_readw(map->map_priv_1 + ofs); +} + +static __u32 epxa_read32(struct map_info *map, unsigned long ofs) +{ + return __raw_readl(map->map_priv_1 + ofs); +} + +static void epxa_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +} + +static void epxa_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + __raw_writeb(d, map->map_priv_1 + adr); + mb(); +} + +static void epxa_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + __raw_writew(d, map->map_priv_1 + adr); + mb(); +} + +static void epxa_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + __raw_writel(d, map->map_priv_1 + adr); + mb(); +} + +static void epxa_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy_toio((void *)(map->map_priv_1 + to), from, len); +} + + + +static struct map_info epxa_map = { + name: "EPXA flash", + size: FLASH_SIZE, + buswidth: 2, + read8: epxa_read8, + read16: epxa_read16, + read32: epxa_read32, + copy_from: epxa_copy_from, + write8: epxa_write8, + write16: epxa_write16, + write32: epxa_write32, + copy_to: epxa_copy_to +}; + + +static int __init epxa_mtd_init(void) +{ + int i; + + printk(KERN_NOTICE "%s flash device: %x at %x\n", BOARD_NAME, FLASH_SIZE, FLASH_START); + epxa_map.map_priv_1 = (unsigned long)ioremap(FLASH_START, FLASH_SIZE); + if (!epxa_map.map_priv_1) { + printk("Failed to ioremap %s flash\n",BOARD_NAME); + return -EIO; + } + + mymtd = do_map_probe("cfi_probe", &epxa_map); + if (!mymtd) { + iounmap((void *)epxa_map.map_priv_1); + return -ENXIO; + } + + mymtd->module = THIS_MODULE; + + /* Unlock the flash device. */ + if(mymtd->unlock){ + for (i=0; inumeraseregions;i++){ + int j; + for(j=0;jeraseregions[i].numblocks;j++){ + mymtd->unlock(mymtd,mymtd->eraseregions[i].offset + j * mymtd->eraseregions[i].erasesize,mymtd->eraseregions[i].erasesize); + } + } + } + +#ifdef CONFIG_MTD_REDBOOT_PARTS + nr_parts = parse_redboot_partitions(mymtd, &parts); + + if (nr_parts > 0) { + add_mtd_partitions(mymtd, parts, nr_parts); + return 0; + } +#endif +#ifdef CONFIG_MTD_AFS_PARTS + nr_parts = parse_afs_partitions(mymtd, &parts); + + if (nr_parts > 0) { + add_mtd_partitions(mymtd, parts, nr_parts); + return 0; + } +#endif + + /* No recognised partitioning schemes found - use defaults */ + nr_parts = epxa_default_partitions(mymtd, &parts); + if (nr_parts > 0) { + add_mtd_partitions(mymtd, parts, nr_parts); + return 0; + } + + /* If all else fails... */ + add_mtd_device(mymtd); + return 0; +} + +static void __exit epxa_mtd_cleanup(void) +{ + if (mymtd) { + if (nr_parts) + del_mtd_partitions(mymtd); + else + del_mtd_device(mymtd); + map_destroy(mymtd); + } + if (epxa_map.map_priv_1) { + iounmap((void *)epxa_map.map_priv_1); + epxa_map.map_priv_1 = 0; + } +} + + +/* + * This will do for now, once we decide which bootldr we're finally + * going to use then we'll remove this function and do it properly + * + * Partions are currently (as offsets from base of flash): + * 0x00000000 - 0x003FFFFF - bootloader (!) + * 0x00400000 - 0x00FFFFFF - Flashdisk + */ + +static int __init epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts) +{ + struct mtd_partition *parts; + int ret, i; + int npartitions = 0; + char *names; + const char *name = "jffs"; + + printk("Using default partitions for %s\n",BOARD_NAME); + npartitions=1; + parts = kmalloc(npartitions*sizeof(*parts)+strlen(name), GFP_KERNEL); + memzero(parts,npartitions*sizeof(*parts)+strlen(name)); + if (!parts) { + ret = -ENOMEM; + goto out; + } + i=0; + names = (char *)&parts[npartitions]; + parts[i].name = names; + names += strlen(name) + 1; + strcpy(parts[i].name, name); + +#ifdef CONFIG_EPXA10DB + parts[i].size = FLASH_SIZE-0x00400000; + parts[i].offset = 0x00400000; +#else + parts[i].size = FLASH_SIZE-0x00180000; + parts[i].offset = 0x00180000; +#endif + + out: + *pparts = parts; + return npartitions; +} + + +module_init(epxa_mtd_init); +module_exit(epxa_mtd_cleanup); + +MODULE_AUTHOR("Clive Davies"); +MODULE_DESCRIPTION("Altera epxa mtd flash map"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/fortunet.c b/drivers/mtd/maps/fortunet.c new file mode 100644 index 000000000000..98fd322e9523 --- /dev/null +++ b/drivers/mtd/maps/fortunet.c @@ -0,0 +1,309 @@ +/* fortunet.c memory map + * + * $Id: fortunet.c,v 1.2 2002/10/14 12:50:22 rmk Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_NUM_REGIONS 4 +#define MAX_NUM_PARTITIONS 8 + +#define DEF_WINDOW_ADDR_PHY 0x00000000 +#define DEF_WINDOW_SIZE 0x00800000 // 8 Mega Bytes + +#define MTD_FORTUNET_PK "MTD FortuNet: " + +#define MAX_NAME_SIZE 128 + +struct map_region +{ + int window_addr_phyical; + int altbuswidth; + struct map_info map_info; + struct mtd_info *mymtd; + struct mtd_partition parts[MAX_NUM_PARTITIONS]; + char map_name[MAX_NAME_SIZE]; + char parts_name[MAX_NUM_PARTITIONS][MAX_NAME_SIZE]; +}; + +static struct map_region map_regions[MAX_NUM_REGIONS]; +static int map_regions_set[MAX_NUM_REGIONS] = {0,0,0,0}; +static int map_regions_parts[MAX_NUM_REGIONS] = {0,0,0,0}; + + +__u8 fortunet_read8(struct map_info *map, unsigned long ofs) +{ + return *(__u8 *)(map->map_priv_1 + ofs); +} + +__u16 fortunet_read16(struct map_info *map, unsigned long ofs) +{ + return *(__u16 *)(map->map_priv_1 + ofs); +} + +__u32 fortunet_read32(struct map_info *map, unsigned long ofs) +{ + return *(__u32 *)(map->map_priv_1 + ofs); +} + +void fortunet_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy(to, (void *)(map->map_priv_1 + from), len); +} + +void fortunet_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + *(__u8 *)(map->map_priv_1 + adr) = d; +} + +void fortunet_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + *(__u16 *)(map->map_priv_1 + adr) = d; +} + +void fortunet_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + *(__u32 *)(map->map_priv_1 + adr) = d; +} + +void fortunet_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy((void *)(map->map_priv_1 + to), from, len); +} + +struct map_info default_map = { + size: DEF_WINDOW_SIZE, + buswidth: 4, + read8: fortunet_read8, + read16: fortunet_read16, + read32: fortunet_read32, + copy_from: fortunet_copy_from, + write8: fortunet_write8, + write16: fortunet_write16, + write32: fortunet_write32, + copy_to: fortunet_copy_to +}; + +static char * __init get_string_option(char *dest,int dest_size,char *sor) +{ + if(!dest_size) + return sor; + dest_size--; + while(*sor) + { + if(*sor==',') + { + sor++; + break; + } + else if(*sor=='\"') + { + sor++; + while(*sor) + { + if(*sor=='\"') + { + sor++; + break; + } + *dest = *sor; + dest++; + sor++; + dest_size--; + if(!dest_size) + { + *dest = 0; + return sor; + } + } + } + else + { + *dest = *sor; + dest++; + sor++; + dest_size--; + if(!dest_size) + { + *dest = 0; + return sor; + } + } + } + *dest = 0; + return sor; +} + +static int __init MTD_New_Region(char *line) +{ + char string[MAX_NAME_SIZE]; + int params[6]; + get_options (get_string_option(string,sizeof(string),line),6,params); + if(params[0]<1) + { + printk(MTD_FORTUNET_PK "Bad paramters for MTD Region " + " name,region-number[,base,size,buswidth,altbuswidth]\n"); + return 1; + } + if((params[1]<0)||(params[1]>=MAX_NUM_REGIONS)) + { + printk(MTD_FORTUNET_PK "Bad region index of %d only have 0..%u regions\n", + params[1],MAX_NUM_REGIONS-1); + return 1; + } + memset(&map_regions[params[1]],0,sizeof(map_regions[params[1]])); + memcpy(&map_regions[params[1]].map_info, + &default_map,sizeof(map_regions[params[1]].map_info)); + map_regions_set[params[1]] = 1; + map_regions[params[1]].window_addr_phyical = DEF_WINDOW_ADDR_PHY; + map_regions[params[1]].altbuswidth = 2; + map_regions[params[1]].mymtd = NULL; + map_regions[params[1]].map_info.name = map_regions[params[1]].map_name; + strcpy(map_regions[params[1]].map_info.name,string); + if(params[0]>1) + { + map_regions[params[1]].window_addr_phyical = params[2]; + } + if(params[0]>2) + { + map_regions[params[1]].map_info.size = params[3]; + } + if(params[0]>3) + { + map_regions[params[1]].map_info.buswidth = params[4]; + } + if(params[0]>4) + { + map_regions[params[1]].altbuswidth = params[5]; + } + return 1; +} + +static int __init MTD_New_Partion(char *line) +{ + char string[MAX_NAME_SIZE]; + int params[4]; + get_options (get_string_option(string,sizeof(string),line),4,params); + if(params[0]<3) + { + printk(MTD_FORTUNET_PK "Bad paramters for MTD Partion " + " name,region-number,size,offset\n"); + return 1; + } + if((params[1]<0)||(params[1]>=MAX_NUM_REGIONS)) + { + printk(MTD_FORTUNET_PK "Bad region index of %d only have 0..%u regions\n", + params[1],MAX_NUM_REGIONS-1); + return 1; + } + if(map_regions_parts[params[1]]>=MAX_NUM_PARTITIONS) + { + printk(MTD_FORTUNET_PK "Out of space for partion in this region\n"); + return 1; + } + map_regions[params[1]].parts[map_regions_parts[params[1]]].name = + map_regions[params[1]]. parts_name[map_regions_parts[params[1]]]; + strcpy(map_regions[params[1]].parts[map_regions_parts[params[1]]].name,string); + map_regions[params[1]].parts[map_regions_parts[params[1]]].size = + params[2]; + map_regions[params[1]].parts[map_regions_parts[params[1]]].offset = + params[3]; + map_regions[params[1]].parts[map_regions_parts[params[1]]].mask_flags = 0; + map_regions_parts[params[1]]++; + return 1; +} + +__setup("MTD_Region=", MTD_New_Region); +__setup("MTD_Partion=", MTD_New_Partion); + +int __init init_fortunet(void) +{ + int ix,iy; + for(iy=ix=0;ixmodule = THIS_MODULE; + add_mtd_partitions(map_regions[ix].mymtd, + map_regions[ix].parts,map_regions_parts[ix]); + } + } + if(iy) + return 0; + return -ENXIO; +} + +static void __exit cleanup_fortunet(void) +{ + int ix; + for(ix=0;ix +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MTD_PARTITIONS +#include +#endif + +#define WINDOW_ADDR0 0x00000000 /* physical properties of flash */ +#define WINDOW_SIZE0 0x00800000 +#define WINDOW_ADDR1 0x10000000 /* physical properties of flash */ +#define WINDOW_SIZE1 0x00800000 +#define NUM_FLASHBANKS 2 +#define BUSWIDTH 4 + +/* can be { "cfi_probe", "jedec_probe", "map_rom", 0 }; */ +#define PROBETYPES { "jedec_probe", 0 } + +#define MSG_PREFIX "impA7:" /* prefix for our printk()'s */ +#define MTDID "impa7-%d" /* for mtdparts= partitioning */ + +static struct mtd_info *impa7_mtd[NUM_FLASHBANKS] = { 0 }; + +__u8 impa7_read8(struct map_info *map, unsigned long ofs) +{ + return __raw_readb(map->map_priv_1 + ofs); +} + +__u16 impa7_read16(struct map_info *map, unsigned long ofs) +{ + return __raw_readw(map->map_priv_1 + ofs); +} + +__u32 impa7_read32(struct map_info *map, unsigned long ofs) +{ + return __raw_readl(map->map_priv_1 + ofs); +} + +void impa7_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, map->map_priv_1 + from, len); +} + +void impa7_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + __raw_writeb(d, map->map_priv_1 + adr); + mb(); +} + +void impa7_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + __raw_writew(d, map->map_priv_1 + adr); + mb(); +} + +void impa7_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + __raw_writel(d, map->map_priv_1 + adr); + mb(); +} + +void impa7_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy_toio(map->map_priv_1 + to, from, len); +} + +static struct map_info impa7_map[NUM_FLASHBANKS] = { + { + name: "impA7 NOR Flash Bank #0", + size: WINDOW_SIZE0, + buswidth: BUSWIDTH, + read8: impa7_read8, + read16: impa7_read16, + read32: impa7_read32, + copy_from: impa7_copy_from, + write8: impa7_write8, + write16: impa7_write16, + write32: impa7_write32, + copy_to: impa7_copy_to + }, + { + name: "impA7 NOR Flash Bank #1", + size: WINDOW_SIZE1, + buswidth: BUSWIDTH, + read8: impa7_read8, + read16: impa7_read16, + read32: impa7_read32, + copy_from: impa7_copy_from, + write8: impa7_write8, + write16: impa7_write16, + write32: impa7_write32, + copy_to: impa7_copy_to + }, +}; + +#ifdef CONFIG_MTD_PARTITIONS + +/* + * MTD partitioning stuff + */ +static struct mtd_partition static_partitions[] = +{ + { + name: "FileSystem", + size: 0x800000, + offset: 0x00000000 + }, +}; + +#define NB_OF(x) (sizeof (x) / sizeof (x[0])) + +#ifdef CONFIG_MTD_CMDLINE_PARTS +int parse_cmdline_partitions(struct mtd_info *master, + struct mtd_partition **pparts, + const char *mtd_id); +#endif + +#endif + +static int mtd_parts_nb = 0; +static struct mtd_partition *mtd_parts = 0; + +int __init init_impa7(void) +{ + static const char *rom_probe_types[] = PROBETYPES; + const char **type; + const char *part_type = 0; + int i; + static struct { u_long addr; u_long size; } pt[NUM_FLASHBANKS] = { + { WINDOW_ADDR0, WINDOW_SIZE0 }, + { WINDOW_ADDR1, WINDOW_SIZE1 }, + }; + char mtdid[10]; + int devicesfound = 0; + + for(i=0; imodule = THIS_MODULE; + add_mtd_device(impa7_mtd[i]); + devicesfound++; +#ifdef CONFIG_MTD_PARTITIONS +#ifdef CONFIG_MTD_CMDLINE_PARTS + sprintf(mtdid, MTDID, i); + mtd_parts_nb = parse_cmdline_partitions(impa7_mtd[i], + &mtd_parts, + mtdid); + if (mtd_parts_nb > 0) + part_type = "command line"; +#endif + if (mtd_parts_nb <= 0) + { + mtd_parts = static_partitions; + mtd_parts_nb = NB_OF(static_partitions); + part_type = "static"; + } + if (mtd_parts_nb <= 0) + { + printk(KERN_NOTICE MSG_PREFIX + "no partition info available\n"); + } + else + { + printk(KERN_NOTICE MSG_PREFIX + "using %s partition definition\n", + part_type); + add_mtd_partitions(impa7_mtd[i], + mtd_parts, mtd_parts_nb); + } +#endif + } + else + iounmap((void *)impa7_map[i].map_priv_1); + } + return devicesfound == 0 ? -ENXIO : 0; +} + +static void __exit cleanup_impa7(void) +{ + int i; + for (i=0; i"); +MODULE_DESCRIPTION("MTD map driver for implementa impA7"); diff --git a/drivers/mtd/maps/iq80310.c b/drivers/mtd/maps/iq80310.c index cb3cb05766d1..3a301135831b 100644 --- a/drivers/mtd/maps/iq80310.c +++ b/drivers/mtd/maps/iq80310.c @@ -1,5 +1,5 @@ /* - * $Id: iq80310.c,v 1.8 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: iq80310.c,v 1.9 2002/01/01 22:45:02 rmk Exp $ * * Mapping for the Intel XScale IQ80310 evaluation board * @@ -116,7 +116,7 @@ static int __init init_iq80310(void) int parsed_nr_parts = 0; char *part_type = "static"; - iq80310_map.map_priv_1 = (unsigned long)__ioremap(WINDOW_ADDR, WINDOW_SIZE, 0); + iq80310_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); if (!iq80310_map.map_priv_1) { printk("Failed to ioremap\n"); return -EIO; @@ -161,7 +161,6 @@ static void __exit cleanup_iq80310(void) } if (iq80310_map.map_priv_1) iounmap((void *)iq80310_map.map_priv_1); - return 0; } module_init(init_iq80310); diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c new file mode 100644 index 000000000000..ccc854980c6f --- /dev/null +++ b/drivers/mtd/maps/pci.c @@ -0,0 +1,385 @@ +/* + * linux/drivers/mtd/maps/pci.c + * + * Copyright (C) 2001 Russell King, All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * $Id: pci.c,v 1.1 2001/09/27 20:28:45 rmk Exp $ + * + * Generic PCI memory map driver. We support the following boards: + * - Intel IQ80310 ATU. + * - Intel EBSA285 (blank rom programming mode). Tested working 27/09/2001 + */ +#include +#include +#include +#include + +#include +#include +#include + +struct map_pci_info; + +struct mtd_pci_info { + int (*init)(struct pci_dev *dev, struct map_pci_info *map); + void (*exit)(struct pci_dev *dev, struct map_pci_info *map); + unsigned long (*translate)(struct map_pci_info *map, unsigned long ofs); + const char *map_name; +}; + +struct map_pci_info { + struct map_info map; + void *base; + void (*exit)(struct pci_dev *dev, struct map_pci_info *map); + unsigned long (*translate)(struct map_pci_info *map, unsigned long ofs); + struct pci_dev *dev; +}; + +/* + * Intel IOP80310 Flash driver + */ + +static int +intel_iq80310_init(struct pci_dev *dev, struct map_pci_info *map) +{ + u32 win_base; + + map->map.buswidth = 1; + map->map.size = 0x00800000; + map->base = ioremap_nocache(pci_resource_start(dev, 0), + pci_resource_len(dev, 0)); + + if (!map->base) + return -ENOMEM; + + /* + * We want to base the memory window at Xscale + * bus address 0, not 0x1000. + */ + pci_read_config_dword(dev, 0x44, &win_base); + pci_write_config_dword(dev, 0x44, 0); + + map->map.map_priv_2 = win_base; + + return 0; +} + +static void +intel_iq80310_exit(struct pci_dev *dev, struct map_pci_info *map) +{ + if (map->base) + iounmap((void *)map->base); + pci_write_config_dword(dev, 0x44, map->map.map_priv_2); +} + +static unsigned long +intel_iq80310_translate(struct map_pci_info *map, unsigned long ofs) +{ + unsigned long page_addr = ofs & 0x00400000; + + /* + * This mundges the flash location so we avoid + * the first 80 bytes (they appear to read nonsense). + */ + if (page_addr) { + writel(0x00000008, map->base + 0x1558); + writel(0x00000000, map->base + 0x1550); + } else { + writel(0x00000007, map->base + 0x1558); + writel(0x00800000, map->base + 0x1550); + ofs += 0x00800000; + } + + return ofs; +} + +static struct mtd_pci_info intel_iq80310_info = { + init: intel_iq80310_init, + exit: intel_iq80310_exit, + translate: intel_iq80310_translate, + map_name: "cfi_probe", +}; + +/* + * Intel DC21285 driver + */ + +static int +intel_dc21285_init(struct pci_dev *dev, struct map_pci_info *map) +{ + unsigned long base, len; + + base = pci_resource_start(dev, PCI_ROM_RESOURCE); + len = pci_resource_len(dev, PCI_ROM_RESOURCE); + + if (!len || !base) { + /* + * No ROM resource + */ + base = pci_resource_start(dev, 2); + len = pci_resource_len(dev, 2); + + /* + * We need to re-allocate PCI BAR2 address range to the + * PCI ROM BAR, and disable PCI BAR2. + */ + } else { + /* + * Hmm, if an address was allocated to the ROM resource, but + * not enabled, should we be allocating a new resource for it + * or simply enabling it? + */ + if (!(pci_resource_flags(dev, PCI_ROM_RESOURCE) & + PCI_ROM_ADDRESS_ENABLE)) { + u32 val; + pci_resource_flags(dev, PCI_ROM_RESOURCE) |= PCI_ROM_ADDRESS_ENABLE; + pci_read_config_dword(dev, PCI_ROM_ADDRESS, &val); + val |= PCI_ROM_ADDRESS_ENABLE; + pci_write_config_dword(dev, PCI_ROM_ADDRESS, val); + printk("%s: enabling expansion ROM\n", dev->slot_name); + } + } + + if (!len || !base) + return -ENXIO; + + map->map.buswidth = 4; + map->map.size = len; + map->base = ioremap_nocache(base, len); + + if (!map->base) + return -ENOMEM; + + return 0; +} + +static void +intel_dc21285_exit(struct pci_dev *dev, struct map_pci_info *map) +{ + u32 val; + + if (map->base) + iounmap((void *)map->base); + + /* + * We need to undo the PCI BAR2/PCI ROM BAR address alteration. + */ + pci_resource_flags(dev, PCI_ROM_RESOURCE) &= ~PCI_ROM_ADDRESS_ENABLE; + pci_read_config_dword(dev, PCI_ROM_ADDRESS, &val); + val &= ~PCI_ROM_ADDRESS_ENABLE; + pci_write_config_dword(dev, PCI_ROM_ADDRESS, val); +} + +static unsigned long +intel_dc21285_translate(struct map_pci_info *map, unsigned long ofs) +{ + return ofs & 0x00ffffc0 ? ofs : (ofs ^ (1 << 5)); +} + +static struct mtd_pci_info intel_dc21285_info = { + init: intel_dc21285_init, + exit: intel_dc21285_exit, + translate: intel_dc21285_translate, + map_name: "jedec_probe", +}; + +/* + * PCI device ID table + */ + +static struct pci_device_id mtd_pci_ids[] __devinitdata = { + { + vendor: PCI_VENDOR_ID_INTEL, + device: 0x530d, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, + class: PCI_CLASS_MEMORY_OTHER << 8, + class_mask: 0xffff00, + driver_data: (unsigned long)&intel_iq80310_info, + }, + { + vendor: PCI_VENDOR_ID_DEC, + device: PCI_DEVICE_ID_DEC_21285, + subvendor: 0, /* DC21285 defaults to 0 on reset */ + subdevice: 0, /* DC21285 defaults to 0 on reset */ + class: 0, + class_mask: 0, + driver_data: (unsigned long)&intel_dc21285_info, + }, + { 0, } +}; + +/* + * Generic code follows. + */ + +static u8 mtd_pci_read8(struct map_info *_map, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + u8 val = readb(map->base + map->translate(map, ofs)); +// printk("read8 : %08lx => %02x\n", ofs, val); + return val; +} + +static u16 mtd_pci_read16(struct map_info *_map, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + u16 val = readw(map->base + map->translate(map, ofs)); +// printk("read16: %08lx => %04x\n", ofs, val); + return val; +} + +static u32 mtd_pci_read32(struct map_info *_map, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + u32 val = readl(map->base + map->translate(map, ofs)); +// printk("read32: %08lx => %08x\n", ofs, val); + return val; +} + +static void mtd_pci_copyfrom(struct map_info *_map, void *to, unsigned long from, ssize_t len) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + memcpy_fromio(to, map->base + map->translate(map, from), len); +} + +static void mtd_pci_write8(struct map_info *_map, u8 val, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; +// printk("write8 : %08lx <= %02x\n", ofs, val); + writeb(val, map->base + map->translate(map, ofs)); +} + +static void mtd_pci_write16(struct map_info *_map, u16 val, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; +// printk("write16: %08lx <= %04x\n", ofs, val); + writew(val, map->base + map->translate(map, ofs)); +} + +static void mtd_pci_write32(struct map_info *_map, u32 val, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; +// printk("write32: %08lx <= %08x\n", ofs, val); + writel(val, map->base + map->translate(map, ofs)); +} + +static void mtd_pci_copyto(struct map_info *_map, unsigned long to, const void *from, ssize_t len) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + memcpy_toio(map->base + map->translate(map, to), from, len); +} + +static struct map_info mtd_pci_map = { + read8: mtd_pci_read8, + read16: mtd_pci_read16, + read32: mtd_pci_read32, + copy_from: mtd_pci_copyfrom, + write8: mtd_pci_write8, + write16: mtd_pci_write16, + write32: mtd_pci_write32, + copy_to: mtd_pci_copyto, +}; + +static int __devinit +mtd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct mtd_pci_info *info = (struct mtd_pci_info *)id->driver_data; + struct map_pci_info *map = NULL; + struct mtd_info *mtd = NULL; + int err; + + err = pci_enable_device(dev); + if (err) + goto out; + + err = pci_request_regions(dev, "pci mtd"); + if (err) + goto out; + + map = kmalloc(sizeof(*map), GFP_KERNEL); + err = -ENOMEM; + if (!map) + goto release; + + map->map = mtd_pci_map; + map->map.name = dev->slot_name; + map->dev = dev; + map->exit = info->exit; + map->translate = info->translate; + + err = info->init(dev, map); + if (err) + goto release; + + /* tsk - do_map_probe should take const char * */ + mtd = do_map_probe((char *)info->map_name, &map->map); + err = -ENODEV; + if (!mtd) + goto release; + + mtd->module = THIS_MODULE; + add_mtd_device(mtd); + + pci_set_drvdata(dev, mtd); + + return 0; + +release: + if (mtd) + map_destroy(mtd); + + if (map) { + map->exit(dev, map); + kfree(map); + } + + pci_release_regions(dev); +out: + return err; +} + +static void __devexit +mtd_pci_remove(struct pci_dev *dev) +{ + struct mtd_info *mtd = pci_get_drvdata(dev); + struct map_pci_info *map = mtd->priv; + + del_mtd_device(mtd); + map_destroy(mtd); + map->exit(dev, map); + kfree(map); + + pci_set_drvdata(dev, NULL); + pci_release_regions(dev); +} + +static struct pci_driver mtd_pci_driver = { + name: "MTD PCI", + probe: mtd_pci_probe, + remove: mtd_pci_remove, + id_table: mtd_pci_ids, +}; + +static int __init mtd_pci_maps_init(void) +{ + return pci_module_init(&mtd_pci_driver); +} + +static void __exit mtd_pci_maps_exit(void) +{ + pci_unregister_driver(&mtd_pci_driver); +} + +module_init(mtd_pci_maps_init); +module_exit(mtd_pci_maps_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("Generic PCI map driver"); +MODULE_DEVICE_TABLE(pci, mtd_pci_ids); + diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c new file mode 100644 index 000000000000..fb87cdd8b873 --- /dev/null +++ b/drivers/mtd/maps/pcmciamtd.c @@ -0,0 +1,893 @@ +/* + * $Id: pcmciamtd.c,v 1.36 2002/10/14 18:49:12 rmk Exp $ + * + * pcmciamtd.c - MTD driver for PCMCIA flash memory cards + * + * Author: Simon Evans + * + * Copyright (C) 2002 Simon Evans + * + * Licence: GPL + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef CONFIG_MTD_DEBUG +static int debug = CONFIG_MTD_DEBUG_VERBOSE; +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Set Debug Level 0=quiet, 5=noisy"); +#undef DEBUG +#define DEBUG(n, format, arg...) \ + if (n <= debug) { \ + printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __FUNCTION__ , ## arg); \ + } + +#else +#undef DEBUG +#define DEBUG(n, arg...) +static const int debug = 0; +#endif + +#define err(format, arg...) printk(KERN_ERR __FILE__ ": " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO __FILE__ ": " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING __FILE__ ": " format "\n" , ## arg) + + +#define DRIVER_DESC "PCMCIA Flash memory card driver" +#define DRIVER_VERSION "$Revision: 1.36 $" + +/* Size of the PCMCIA address space: 26 bits = 64 MB */ +#define MAX_PCMCIA_ADDR 0x4000000 + +struct pcmciamtd_dev { + struct list_head list; + dev_link_t link; /* PCMCIA link */ + caddr_t win_base; /* ioremapped address of PCMCIA window */ + unsigned int win_size; /* size of window */ + unsigned int cardsize; /* size of whole card */ + unsigned int offset; /* offset into card the window currently points at */ + struct map_info pcmcia_map; + struct mtd_info *mtd_info; + u8 vpp; + char mtd_name[sizeof(struct cistpl_vers_1_t)]; +}; + + +static dev_info_t dev_info = "pcmciamtd"; +static LIST_HEAD(dev_list); + +/* Module parameters */ + +/* 2 = do 16-bit transfers, 1 = do 8-bit transfers */ +static int buswidth = 2; + +/* Speed of memory accesses, in ns */ +static int mem_speed; + +/* Force the size of an SRAM card */ +static int force_size; + +/* Force Vpp */ +static int vpp; + +/* Set Vpp */ +static int setvpp; + +/* Force card to be treated as FLASH, ROM or RAM */ +static int mem_type; + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Simon Evans "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_PARM(buswidth, "i"); +MODULE_PARM_DESC(buswidth, "Set buswidth (1=8 bit, 2=16 bit, default=2)"); +MODULE_PARM(mem_speed, "i"); +MODULE_PARM_DESC(mem_speed, "Set memory access speed in ns"); +MODULE_PARM(force_size, "i"); +MODULE_PARM_DESC(force_size, "Force size of card in MB (1-64)"); +MODULE_PARM(setvpp, "i"); +MODULE_PARM_DESC(setvpp, "Set Vpp (0=Never, 1=On writes, 2=Always on, default=0)"); +MODULE_PARM(vpp, "i"); +MODULE_PARM_DESC(vpp, "Vpp value in 1/10ths eg 33=3.3V 120=12V (Dangerous)"); +MODULE_PARM(mem_type, "i"); +MODULE_PARM_DESC(mem_type, "Set Memory type (0=Flash, 1=RAM, 2=ROM, default=0)"); + + + +static void inline cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + + +/* read/write{8,16} copy_{from,to} routines with window remapping to access whole card */ + +static caddr_t remap_window(struct map_info *map, unsigned long to) +{ + struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; + window_handle_t win = (window_handle_t)map->map_priv_2; + memreq_t mrq; + int ret; + + mrq.CardOffset = to & ~(dev->win_size-1); + if(mrq.CardOffset != dev->offset) { + DEBUG(2, "Remapping window from 0x%8.8x to 0x%8.8x", + dev->offset, mrq.CardOffset); + mrq.Page = 0; + if( (ret = CardServices(MapMemPage, win, &mrq)) != CS_SUCCESS) { + cs_error(dev->link.handle, MapMemPage, ret); + return NULL; + } + dev->offset = mrq.CardOffset; + } + return dev->win_base + (to & (dev->win_size-1)); +} + + +static u8 pcmcia_read8_remap(struct map_info *map, unsigned long ofs) +{ + caddr_t addr; + u8 d; + + addr = remap_window(map, ofs); + if(!addr) + return 0; + + d = readb(addr); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, addr, d); + return d; +} + + +static u16 pcmcia_read16_remap(struct map_info *map, unsigned long ofs) +{ + caddr_t addr; + u16 d; + + addr = remap_window(map, ofs); + if(!addr) + return 0; + + d = readw(addr); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, addr, d); + return d; +} + + +static void pcmcia_copy_from_remap(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; + unsigned long win_size = dev->win_size; + + DEBUG(3, "to = %p from = %lu len = %u", to, from, len); + while(len) { + int toread = win_size - (from & (win_size-1)); + caddr_t addr; + + if(toread > len) + toread = len; + + addr = remap_window(map, from); + if(!addr) + return; + + DEBUG(4, "memcpy from %p to %p len = %d", addr, to, toread); + memcpy_fromio(to, addr, toread); + len -= toread; + to += toread; + from += toread; + } +} + + +static void pcmcia_write8_remap(struct map_info *map, u8 d, unsigned long adr) +{ + caddr_t addr = remap_window(map, adr); + + if(!addr) + return; + + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02x", adr, addr, d); + writeb(d, addr); +} + + +static void pcmcia_write16_remap(struct map_info *map, u16 d, unsigned long adr) +{ + caddr_t addr = remap_window(map, adr); + if(!addr) + return; + + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04x", adr, addr, d); + writew(d, addr); +} + + +static void pcmcia_copy_to_remap(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; + unsigned long win_size = dev->win_size; + + DEBUG(3, "to = %lu from = %p len = %u", to, from, len); + while(len) { + int towrite = win_size - (to & (win_size-1)); + caddr_t addr; + + if(towrite > len) + towrite = len; + + addr = remap_window(map, to); + if(!addr) + return; + + DEBUG(4, "memcpy from %p to %p len = %d", from, addr, towrite); + memcpy_toio(addr, from, towrite); + len -= towrite; + to += towrite; + from += towrite; + } +} + + +/* read/write{8,16} copy_{from,to} routines with direct access */ + +static u8 pcmcia_read8(struct map_info *map, unsigned long ofs) +{ + caddr_t win_base = (caddr_t)map->map_priv_2; + u8 d; + + d = readb(win_base + ofs); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, win_base + ofs, d); + return d; +} + + +static u16 pcmcia_read16(struct map_info *map, unsigned long ofs) +{ + caddr_t win_base = (caddr_t)map->map_priv_2; + u16 d; + + d = readw(win_base + ofs); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, win_base + ofs, d); + return d; +} + + +static void pcmcia_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + caddr_t win_base = (caddr_t)map->map_priv_2; + + DEBUG(3, "to = %p from = %lu len = %u", to, from, len); + memcpy_fromio(to, win_base + from, len); +} + + +static void pcmcia_write8(struct map_info *map, u8 d, unsigned long adr) +{ + caddr_t win_base = (caddr_t)map->map_priv_2; + + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02x", adr, win_base + adr, d); + writeb(d, win_base + adr); +} + + +static void pcmcia_write16(struct map_info *map, u16 d, unsigned long adr) +{ + caddr_t win_base = (caddr_t)map->map_priv_2; + + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04x", adr, win_base + adr, d); + writew(d, win_base + adr); +} + + +static void pcmcia_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + caddr_t win_base = (caddr_t)map->map_priv_2; + + DEBUG(3, "to = %lu from = %p len = %u", to, from, len); + memcpy_toio(win_base + to, from, len); +} + + +static void pcmciamtd_set_vpp(struct map_info *map, int on) +{ + struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; + dev_link_t *link = &dev->link; + modconf_t mod; + int ret; + + mod.Attributes = CONF_VPP1_CHANGE_VALID | CONF_VPP2_CHANGE_VALID; + mod.Vcc = 0; + mod.Vpp1 = mod.Vpp2 = on ? dev->vpp : 0; + + DEBUG(2, "dev = %p on = %d vpp = %d\n", dev, on, dev->vpp); + ret = CardServices(ModifyConfiguration, link->handle, &mod); + if(ret != CS_SUCCESS) { + cs_error(link->handle, ModifyConfiguration, ret); + } +} + + +/* After a card is removed, pcmciamtd_release() will unregister the + * device, and release the PCMCIA configuration. If the device is + * still open, this will be postponed until it is closed. + */ + +static void pcmciamtd_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + struct pcmciamtd_dev *dev = NULL; + int ret; + struct list_head *temp1, *temp2; + + DEBUG(3, "link = 0x%p", link); + /* Find device in list */ + list_for_each_safe(temp1, temp2, &dev_list) { + dev = list_entry(temp1, struct pcmciamtd_dev, list); + if(link == &dev->link) + break; + } + if(link != &dev->link) { + DEBUG(1, "Cant find %p in dev_list", link); + return; + } + + if(dev) { + if(dev->mtd_info) { + del_mtd_device(dev->mtd_info); + dev->mtd_info = NULL; + MOD_DEC_USE_COUNT; + } + if (link->win) { + if(dev->win_base) { + iounmap(dev->win_base); + dev->win_base = NULL; + } + CardServices(ReleaseWindow, link->win); + } + ret = CardServices(ReleaseConfiguration, link->handle); + if(ret != CS_SUCCESS) + cs_error(link->handle, ReleaseConfiguration, ret); + + } + link->state &= ~DEV_CONFIG; +} + + +static void card_settings(struct pcmciamtd_dev *dev, dev_link_t *link, int *new_name) +{ + int rc; + tuple_t tuple; + cisparse_t parse; + u_char buf[64]; + + tuple.Attributes = 0; + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleDataMax = sizeof(buf); + tuple.TupleOffset = 0; + tuple.DesiredTuple = RETURN_FIRST_TUPLE; + + rc = CardServices(GetFirstTuple, link->handle, &tuple); + while(rc == CS_SUCCESS) { + rc = CardServices(GetTupleData, link->handle, &tuple); + if(rc != CS_SUCCESS) { + cs_error(link->handle, GetTupleData, rc); + break; + } + rc = CardServices(ParseTuple, link->handle, &tuple, &parse); + if(rc != CS_SUCCESS) { + cs_error(link->handle, ParseTuple, rc); + break; + } + + switch(tuple.TupleCode) { + case CISTPL_FORMAT: { + cistpl_format_t *t = &parse.format; + (void)t; /* Shut up, gcc */ + DEBUG(2, "Format type: %u, Error Detection: %u, offset = %u, length =%u", + t->type, t->edc, t->offset, t->length); + break; + + } + + case CISTPL_DEVICE: { + cistpl_device_t *t = &parse.device; + int i; + DEBUG(2, "Common memory:"); + dev->pcmcia_map.size = t->dev[0].size; + for(i = 0; i < t->ndev; i++) { + DEBUG(2, "Region %d, type = %u", i, t->dev[i].type); + DEBUG(2, "Region %d, wp = %u", i, t->dev[i].wp); + DEBUG(2, "Region %d, speed = %u ns", i, t->dev[i].speed); + DEBUG(2, "Region %d, size = %u bytes", i, t->dev[i].size); + } + break; + } + + case CISTPL_VERS_1: { + cistpl_vers_1_t *t = &parse.version_1; + int i; + if(t->ns) { + dev->mtd_name[0] = '\0'; + for(i = 0; i < t->ns; i++) { + if(i) + strcat(dev->mtd_name, " "); + strcat(dev->mtd_name, t->str+t->ofs[i]); + } + } + DEBUG(2, "Found name: %s", dev->mtd_name); + break; + } + + case CISTPL_JEDEC_C: { + cistpl_jedec_t *t = &parse.jedec; + int i; + for(i = 0; i < t->nid; i++) { + DEBUG(2, "JEDEC: 0x%02x 0x%02x", t->id[i].mfr, t->id[i].info); + } + break; + } + + case CISTPL_DEVICE_GEO: { + cistpl_device_geo_t *t = &parse.device_geo; + int i; + dev->pcmcia_map.buswidth = t->geo[0].buswidth; + for(i = 0; i < t->ngeo; i++) { + DEBUG(2, "region: %d buswidth = %u", i, t->geo[i].buswidth); + DEBUG(2, "region: %d erase_block = %u", i, t->geo[i].erase_block); + DEBUG(2, "region: %d read_block = %u", i, t->geo[i].read_block); + DEBUG(2, "region: %d write_block = %u", i, t->geo[i].write_block); + DEBUG(2, "region: %d partition = %u", i, t->geo[i].partition); + DEBUG(2, "region: %d interleave = %u", i, t->geo[i].interleave); + } + break; + } + + default: + DEBUG(2, "Unknown tuple code %d", tuple.TupleCode); + } + + rc = CardServices(GetNextTuple, link->handle, &tuple, &parse); + } + if(!dev->pcmcia_map.size) + dev->pcmcia_map.size = MAX_PCMCIA_ADDR; + + if(!dev->pcmcia_map.buswidth) + dev->pcmcia_map.buswidth = 2; + + if(force_size) { + dev->pcmcia_map.size = force_size << 20; + DEBUG(2, "size forced to %dM", force_size); + + } + + if(buswidth) { + dev->pcmcia_map.buswidth = buswidth; + DEBUG(2, "buswidth forced to %d", buswidth); + } + + dev->pcmcia_map.name = dev->mtd_name; + if(!dev->mtd_name[0]) { + strcpy(dev->mtd_name, "PCMCIA Memory card"); + *new_name = 1; + } + + DEBUG(1, "Device: Size: %lu Width:%d Name: %s", + dev->pcmcia_map.size, dev->pcmcia_map.buswidth << 3, dev->mtd_name); +} + + +/* pcmciamtd_config() is scheduled to run after a CARD_INSERTION event + * is received, to configure the PCMCIA socket, and to make the + * MTD device available to the system. + */ + +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed + +static void pcmciamtd_config(dev_link_t *link) +{ + struct pcmciamtd_dev *dev = link->priv; + struct mtd_info *mtd = NULL; + cs_status_t status; + win_req_t req; + int last_ret = 0, last_fn = 0; + int ret; + int i; + config_info_t t; + static char *probes[] = { "jedec_probe", "cfi_probe" }; + cisinfo_t cisinfo; + int new_name = 0; + + DEBUG(3, "link=0x%p", link); + + /* Configure card */ + link->state |= DEV_CONFIG; + + DEBUG(2, "Validating CIS"); + ret = CardServices(ValidateCIS, link->handle, &cisinfo); + if(ret != CS_SUCCESS) { + cs_error(link->handle, GetTupleData, ret); + } else { + DEBUG(2, "ValidateCIS found %d chains", cisinfo.Chains); + } + + card_settings(dev, link, &new_name); + + dev->pcmcia_map.read8 = pcmcia_read8_remap; + dev->pcmcia_map.read16 = pcmcia_read16_remap; + dev->pcmcia_map.copy_from = pcmcia_copy_from_remap; + dev->pcmcia_map.write8 = pcmcia_write8_remap; + dev->pcmcia_map.write16 = pcmcia_write16_remap; + dev->pcmcia_map.copy_to = pcmcia_copy_to_remap; + if(setvpp == 1) + dev->pcmcia_map.set_vpp = pcmciamtd_set_vpp; + + /* Request a memory window for PCMCIA. Some architeures can map windows upto the maximum + that PCMCIA can support (64Mb) - this is ideal and we aim for a window the size of the + whole card - otherwise we try smaller windows until we succeed */ + + req.Attributes = WIN_MEMORY_TYPE_CM | WIN_ENABLE; + req.Attributes |= (dev->pcmcia_map.buswidth == 1) ? WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16; + req.Base = 0; + req.AccessSpeed = mem_speed; + link->win = (window_handle_t)link->handle; + req.Size = (force_size) ? force_size << 20 : MAX_PCMCIA_ADDR; + dev->win_size = 0; + + do { + int ret; + DEBUG(2, "requesting window with size = %dKB memspeed = %d", + req.Size >> 10, req.AccessSpeed); + link->win = (window_handle_t)link->handle; + ret = CardServices(RequestWindow, &link->win, &req); + DEBUG(2, "ret = %d dev->win_size = %d", ret, dev->win_size); + if(ret) { + req.Size >>= 1; + } else { + DEBUG(2, "Got window of size %dKB", req.Size >> 10); + dev->win_size = req.Size; + break; + } + } while(req.Size >= 0x1000); + + DEBUG(2, "dev->win_size = %d", dev->win_size); + + if(!dev->win_size) { + err("Cant allocate memory window"); + pcmciamtd_release((u_long)link); + return; + } + DEBUG(1, "Allocated a window of %dKB", dev->win_size >> 10); + + /* Get write protect status */ + CS_CHECK(GetStatus, link->handle, &status); + DEBUG(2, "status value: 0x%x window handle = 0x%8.8lx", + status.CardState, (unsigned long)link->win); + dev->win_base = ioremap(req.Base, req.Size); + if(!dev->win_base) { + err("ioremap(%lu, %u) failed", req.Base, req.Size); + pcmciamtd_release((u_long)link); + return; + } + DEBUG(1, "mapped window dev = %p req.base = 0x%lx base = %p size = 0x%x", + dev, req.Base, dev->win_base, req.Size); + dev->cardsize = 0; + dev->offset = 0; + + dev->pcmcia_map.map_priv_1 = (unsigned long)dev; + dev->pcmcia_map.map_priv_2 = (unsigned long)link->win; + + DEBUG(2, "Getting configuration"); + CS_CHECK(GetConfigurationInfo, link->handle, &t); + DEBUG(2, "Vcc = %d Vpp1 = %d Vpp2 = %d", t.Vcc, t.Vpp1, t.Vpp2); + dev->vpp = (vpp) ? vpp : t.Vpp1; + link->conf.Attributes = 0; + link->conf.Vcc = t.Vcc; + if(setvpp == 2) { + link->conf.Vpp1 = dev->vpp; + link->conf.Vpp2 = dev->vpp; + } else { + link->conf.Vpp1 = 0; + link->conf.Vpp2 = 0; + } + + link->conf.IntType = INT_MEMORY; + link->conf.ConfigBase = t.ConfigBase; + link->conf.Status = t.Status; + link->conf.Pin = t.Pin; + link->conf.Copy = t.Copy; + link->conf.ExtStatus = t.ExtStatus; + link->conf.ConfigIndex = 0; + link->conf.Present = t.Present; + DEBUG(2, "Setting Configuration"); + ret = CardServices(RequestConfiguration, link->handle, &link->conf); + if(ret != CS_SUCCESS) { + cs_error(link->handle, RequestConfiguration, ret); + } + + link->dev = NULL; + link->state &= ~DEV_CONFIG_PENDING; + + if(mem_type == 1) { + mtd = do_map_probe("map_ram", &dev->pcmcia_map); + } else if(mem_type == 2) { + mtd = do_map_probe("map_rom", &dev->pcmcia_map); + } else { + for(i = 0; i < sizeof(probes) / sizeof(char *); i++) { + DEBUG(1, "Trying %s", probes[i]); + mtd = do_map_probe(probes[i], &dev->pcmcia_map); + if(mtd) + break; + + DEBUG(1, "FAILED: %s", probes[i]); + } + } + + if(!mtd) { + DEBUG(1, "Cant find an MTD"); + pcmciamtd_release((u_long)link); + return; + } + + dev->mtd_info = mtd; + mtd->module = THIS_MODULE; + dev->cardsize = mtd->size; + + if(new_name) { + int size = 0; + char unit = ' '; + /* Since we are using a default name, make it better by adding in the + size */ + if(mtd->size < 1048576) { /* <1MB in size, show size in K */ + size = mtd->size >> 10; + unit = 'K'; + } else { + size = mtd->size >> 20; + unit = 'M'; + } + sprintf(mtd->name, "%d%cB %s", size, unit, "PCMCIA Memory card"); + } + + /* If the memory found is fits completely into the mapped PCMCIA window, + use the faster non-remapping read/write functions */ + if(dev->cardsize <= dev->win_size) { + DEBUG(1, "Using non remapping memory functions"); + + dev->pcmcia_map.map_priv_2 = (unsigned long)dev->win_base; + dev->pcmcia_map.read8 = pcmcia_read8; + dev->pcmcia_map.read16 = pcmcia_read16; + dev->pcmcia_map.copy_from = pcmcia_copy_from; + dev->pcmcia_map.write8 = pcmcia_write8; + dev->pcmcia_map.write16 = pcmcia_write16; + dev->pcmcia_map.copy_to = pcmcia_copy_to; + } + + MOD_INC_USE_COUNT; + if(add_mtd_device(mtd)) { + dev->mtd_info = NULL; + MOD_DEC_USE_COUNT; + err("Couldnt register MTD device"); + pcmciamtd_release((u_long)link); + return; + } + DEBUG(1, "mtd added @ %p mtd->priv = %p", mtd, mtd->priv); + + return; + + cs_failed: + cs_error(link->handle, last_fn, last_ret); + err("CS Error, exiting"); + pcmciamtd_release((u_long)link); + return; +} + + +/* The card status event handler. Mostly, this schedules other + * stuff to run after an event is received. A CARD_REMOVAL event + * also sets some flags to discourage the driver from trying + * to talk to the card any more. + */ + +static int pcmciamtd_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + + DEBUG(1, "event=0x%06x", event); + switch (event) { + case CS_EVENT_CARD_REMOVAL: + DEBUG(2, "EVENT_CARD_REMOVAL"); + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) + mod_timer(&link->release, jiffies + HZ/20); + break; + case CS_EVENT_CARD_INSERTION: + DEBUG(2, "EVENT_CARD_INSERTION"); + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + pcmciamtd_config(link); + break; + case CS_EVENT_PM_SUSPEND: + DEBUG(2, "EVENT_PM_SUSPEND"); + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + DEBUG(2, "EVENT_RESET_PHYSICAL"); + /* get_lock(link); */ + break; + case CS_EVENT_PM_RESUME: + DEBUG(2, "EVENT_PM_RESUME"); + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + DEBUG(2, "EVENT_CARD_RESET"); + /* free_lock(link); */ + break; + default: + DEBUG(2, "Unknown event %d", event); + } + return 0; +} + + +/* This deletes a driver "instance". The device is de-registered + * with Card Services. If it has been released, all local data + * structures are freed. Otherwise, the structures will be freed + * when the device is released. + */ + +static void pcmciamtd_detach(dev_link_t *link) +{ + int ret; + struct pcmciamtd_dev *dev = NULL; + struct list_head *temp1, *temp2; + + DEBUG(3, "link=0x%p", link); + + /* Find device in list */ + list_for_each_safe(temp1, temp2, &dev_list) { + dev = list_entry(temp1, struct pcmciamtd_dev, list); + if(link == &dev->link) + break; + } + if(link != &dev->link) { + DEBUG(1, "Cant find %p in dev_list", link); + return; + } + + del_timer(&link->release); + + if(!dev) { + DEBUG(3, "dev is NULL"); + return; + } + + if (link->state & DEV_CONFIG) { + //pcmciamtd_release((u_long)link); + DEBUG(3, "DEV_CONFIG set"); + link->state |= DEV_STALE_LINK; + return; + } + + if (link->handle) { + DEBUG(2, "Deregistering with card services"); + ret = CardServices(DeregisterClient, link->handle); + if (ret != CS_SUCCESS) + cs_error(link->handle, DeregisterClient, ret); + } + DEBUG(3, "Freeing dev (%p)", dev); + list_del(&dev->list); + link->priv = NULL; + kfree(dev); +} + + +/* pcmciamtd_attach() creates an "instance" of the driver, allocating + * local data structures for one device. The device is registered + * with Card Services. + */ + +static dev_link_t *pcmciamtd_attach(void) +{ + struct pcmciamtd_dev *dev; + dev_link_t *link; + client_reg_t client_reg; + int ret; + + /* Create new memory card device */ + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) return NULL; + DEBUG(1, "dev=0x%p", dev); + + memset(dev, 0, sizeof(*dev)); + link = &dev->link; link->priv = dev; + + link->release.function = &pcmciamtd_release; + link->release.data = (u_long)link; + + link->conf.Attributes = 0; + link->conf.IntType = INT_MEMORY; + + list_add(&dev->list, &dev_list); + + /* Register with Card Services */ + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &pcmciamtd_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + DEBUG(2, "Calling RegisterClient"); + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != 0) { + cs_error(link->handle, RegisterClient, ret); + pcmciamtd_detach(link); + return NULL; + } + + return link; +} + + +static int __init init_pcmciamtd(void) +{ + servinfo_t serv; + + info(DRIVER_DESC " " DRIVER_VERSION); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + err("Card Services release does not match!"); + return -1; + } + + if(buswidth && buswidth != 1 && buswidth != 2) { + info("bad buswidth (%d), using default", buswidth); + buswidth = 2; + } + if(force_size && (force_size < 1 || force_size > 64)) { + info("bad force_size (%d), using default", force_size); + force_size = 0; + } + if(mem_type && mem_type != 1 && mem_type != 2) { + info("bad mem_type (%d), using default", mem_type); + mem_type = 0; + } + register_pccard_driver(&dev_info, &pcmciamtd_attach, &pcmciamtd_detach); + return 0; +} + + +static void __exit exit_pcmciamtd(void) +{ + struct list_head *temp1, *temp2; + + DEBUG(1, DRIVER_DESC " unloading"); + unregister_pccard_driver(&dev_info); + list_for_each_safe(temp1, temp2, &dev_list) { + dev_link_t *link = &list_entry(temp1, struct pcmciamtd_dev, list)->link; + if (link && (link->state & DEV_CONFIG)) { + pcmciamtd_release((u_long)link); + pcmciamtd_detach(link); + } + } +} + +module_init(init_pcmciamtd); +module_exit(exit_pcmciamtd); diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index b6c1c0f9efe7..b2592a0a0d63 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -3,28 +3,35 @@ * * (C) 2000 Nicolas Pitre * - * $Id: sa1100-flash.c,v 1.22 2001/10/02 10:04:52 rmk Exp $ + * $Id: sa1100-flash.c,v 1.28 2002/05/07 13:48:38 abz Exp $ */ #include #include #include +#include #include #include #include #include +#include #include +#include #include +#include +#include #ifndef CONFIG_ARCH_SA1100 #error This is for SA1100 architecture only #endif - -#define WINDOW_ADDR 0xe8000000 +/* + * This isnt complete yet, so... + */ +#define CONFIG_MTD_SA1100_STATICMAP 1 static __u8 sa1100_read8(struct map_info *map, unsigned long ofs) { @@ -66,33 +73,7 @@ static void sa1100_copy_to(struct map_info *map, unsigned long to, const void *f memcpy((void *)(map->map_priv_1 + to), from, len); } - -#ifdef CONFIG_SA1100_H3600 - -static void h3600_set_vpp(struct map_info *map, int vpp) -{ - if (vpp) - set_h3600_egpio(EGPIO_H3600_VPP_ON); - else - clr_h3600_egpio(EGPIO_H3600_VPP_ON); -} - -#endif - -#ifdef CONFIG_SA1100_JORNADA720 - -static void jornada720_set_vpp(int vpp) -{ - if (vpp) - PPSR |= 0x80; - else - PPSR &= ~0x80; - PPDR |= 0x80; -} - -#endif - -static struct map_info sa1100_map = { +static struct map_info sa1100_map __initdata = { name: "SA1100 flash", read8: sa1100_read8, read16: sa1100_read16, @@ -102,609 +83,1232 @@ static struct map_info sa1100_map = { write16: sa1100_write16, write32: sa1100_write32, copy_to: sa1100_copy_to, - - map_priv_1: WINDOW_ADDR, }; +#ifdef CONFIG_MTD_SA1100_STATICMAP /* * Here are partition information for all known SA1100-based devices. * See include/linux/mtd/partitions.h for definition of the mtd_partition * structure. - * - * The *_max_flash_size is the maximum possible mapped flash size which - * is not necessarily the actual flash size. It must correspond to the - * value specified in the mapping definition defined by the - * "struct map_desc *_io_desc" for the corresponding machine. + * + * Please note: + * 1. We no longer support static flash mappings via the machine io_desc + * structure. + * 2. The flash size given should be the largest flash size that can + * be accomodated. + * + * The MTD layer will detect flash chip aliasing and reduce the size of + * the map accordingly. + * + * Please keep these in alphabetical order, and formatted as per existing + * entries. Thanks. */ -#ifdef CONFIG_SA1100_ASSABET +#ifdef CONFIG_SA1100_ADSBITSY +static struct mtd_partition adsbitsy_partitions[] = { + { + name: "bootROM", + size: 0x80000, + offset: 0, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "zImage", + size: 0x100000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "ramdisk.gz", + size: 0x300000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "User FS", + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + } +}; +#endif +#ifdef CONFIG_SA1100_ASSABET /* Phase 4 Assabet has two 28F160B3 flash parts in bank 0: */ -static unsigned long assabet4_max_flash_size = 0x00400000; static struct mtd_partition assabet4_partitions[] = { - { - name: "bootloader", - size: 0x00020000, - offset: 0, - mask_flags: MTD_WRITEABLE - },{ - name: "bootloader params", - size: 0x00020000, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE - },{ - name: "jffs", - size: MTDPART_SIZ_FULL, - offset: MTDPART_OFS_APPEND - } + { + name: "bootloader", + size: 0x00020000, + offset: 0, + mask_flags: MTD_WRITEABLE, + }, { + name: "bootloader params", + size: 0x00020000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE, + }, { + name: "jffs", + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + } }; /* Phase 5 Assabet has two 28F128J3A flash parts in bank 0: */ -static unsigned long assabet5_max_flash_size = 0x02000000; static struct mtd_partition assabet5_partitions[] = { - { - name: "bootloader", - size: 0x00040000, - offset: 0, - mask_flags: MTD_WRITEABLE - },{ - name: "bootloader params", - size: 0x00040000, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE - },{ - name: "jffs", - size: MTDPART_SIZ_FULL, - offset: MTDPART_OFS_APPEND - } + { + name: "bootloader", + size: 0x00040000, + offset: 0, + mask_flags: MTD_WRITEABLE, + }, { + name: "bootloader params", + size: 0x00040000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE, + }, { + name: "jffs", + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + } }; -#define assabet_max_flash_size assabet5_max_flash_size -#define assabet_partitions assabet5_partitions - +#define assabet_partitions assabet5_partitions #endif -#ifdef CONFIG_SA1100_FLEXANET - -/* Flexanet has two 28F128J3A flash parts in bank 0: */ -static unsigned long flexanet_max_flash_size = 0x02000000; -static struct mtd_partition flexanet_partitions[] = { - { - name: "bootloader", - size: 0x00040000, - offset: 0, - mask_flags: MTD_WRITEABLE - },{ - name: "bootloader params", - size: 0x00040000, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE - },{ - name: "kernel", - size: 0x000C0000, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE - },{ - name: "altkernel", - size: 0x000C0000, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE - },{ - name: "root", - size: 0x00400000, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE - },{ - name: "free1", - size: 0x00300000, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE - },{ - name: "free2", - size: 0x00300000, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE - },{ - name: "free3", - size: MTDPART_SIZ_FULL, - offset: MTDPART_OFS_APPEND, - mask_flags: MTD_WRITEABLE - } +#ifdef CONFIG_SA1100_BADGE4 +/* + * 1 x Intel 28F320C3BA100 Advanced+ Boot Block Flash (32 Mi bit) + * Eight 4 KiW Parameter Bottom Blocks (64 KiB) + * Sixty-three 32 KiW Main Blocks (4032 Ki b) + */ +static struct mtd_partition badge4_partitions[] = { + { + name: "BLOB boot loader", + offset: 0, + size: 0x0000A000 + }, { + name: "params", + offset: MTDPART_OFS_APPEND, + size: 0x00006000 + }, { + name: "kernel", + offset: MTDPART_OFS_APPEND, + size: 0x00100000 + }, { + name: "root", + offset: MTDPART_OFS_APPEND, + size: MTDPART_SIZ_FULL + } }; - #endif -#ifdef CONFIG_SA1100_HUW_WEBPANEL -static unsigned long huw_webpanel_max_flash_size = 0x01000000; -static struct mtd_partition huw_webpanel_partitions[] = { - { - name: "Loader", - size: 0x00040000, - offset: 0, - },{ - name: "Sector 1", - size: 0x00040000, - offset: MTDPART_OFS_APPEND, - },{ - size: MTDPART_SIZ_FULL, - offset: MTDPART_OFS_APPEND, + +#ifdef CONFIG_SA1100_CERF +#ifdef CONFIG_SA1100_CERF_FLASH_32MB +static struct mtd_partition cerf_partitions[] = { + { + name: "firmware", + size: 0x00040000, + offset: 0, + }, { + name: "params", + size: 0x00040000, + offset: 0x00040000, + }, { + name: "kernel", + size: 0x00100000, + offset: 0x00080000, + }, { + name: "rootdisk", + size: 0x01E80000, + offset: 0x00180000, } }; -#endif /* CONFIG_SA1100_HUW_WEBPANEL */ - - -#ifdef CONFIG_SA1100_H3600 - -static unsigned long h3600_max_flash_size = 0x02000000; -static struct mtd_partition h3600_partitions[] = { +#elif defined CONFIG_SA1100_CERF_FLASH_16MB +static struct mtd_partition cerf_partitions[] = { { - name: "H3600 boot firmware", - size: 0x00040000, - offset: 0, - mask_flags: MTD_WRITEABLE /* force read-only */ - },{ - name: "H3600 kernel", - size: 0x00080000, - offset: 0x40000 - },{ - name: "H3600 params", - size: 0x00040000, - offset: 0xC0000 - },{ -#ifdef CONFIG_JFFS2_FS - name: "H3600 root jffs2", - offset: 0x00100000, - size: MTDPART_SIZ_FULL + name: "firmware", + size: 0x00020000, + offset: 0, + }, { + name: "params", + size: 0x00020000, + offset: 0x00020000, + }, { + name: "kernel", + size: 0x00100000, + offset: 0x00040000, + }, { + name: "rootdisk", + size: 0x00EC0000, + offset: 0x00140000, + } +}; +#elif defined CONFIG_SA1100_CERF_FLASH_8MB +# error "Unwritten type definition" #else - name: "H3600 initrd", - size: 0x00100000, - offset: 0x00100000 - },{ - name: "H3600 root cramfs", - size: 0x00300000, - offset: 0x00200000 - },{ - name: "H3600 usr cramfs", - size: 0x00800000, - offset: 0x00500000 - },{ - name: "H3600 usr local", - offset: 0x00d00000, - size: MTDPART_SIZ_FULL +# error "Undefined memory orientation for CERF in sa1100-flash.c" +#endif #endif + +#ifdef CONFIG_SA1100_CONSUS +static struct mtd_partition consus_partitions[] = { + { + name: "Consus boot firmware", + offset: 0, + size: 0x00040000, + mask_flags: MTD_WRITABLE, /* force read-only */ + }, { + name: "Consus kernel", + offset: 0x00040000, + size: 0x00100000, + mask_flags: 0, + }, { + name: "Consus disk", + offset: 0x00140000, + /* The rest (up to 16M) for jffs. We could put 0 and + make it find the size automatically, but right now + i have 32 megs. jffs will use all 32 megs if given + the chance, and this leads to horrible problems + when you try to re-flash the image because blob + won't erase the whole partition. */ + size: 0x01000000 - 0x00140000, + mask_flags: 0, + }, { + /* this disk is a secondary disk, which can be used as + needed, for simplicity, make it the size of the other + consus partition, although realistically it could be + the remainder of the disk (depending on the file + system used) */ + name: "Consus disk2", + offset: 0x01000000, + size: 0x01000000 - 0x00140000, + mask_flags: 0, } }; +#endif +#ifdef CONFIG_SA1100_FLEXANET +/* Flexanet has two 28F128J3A flash parts in bank 0: */ +#define FLEXANET_FLASH_SIZE 0x02000000 +static struct mtd_partition flexanet_partitions[] = { + { + name: "bootloader", + size: 0x00040000, + offset: 0, + mask_flags: MTD_WRITEABLE, + }, { + name: "bootloader params", + size: 0x00040000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE, + }, { + name: "kernel", + size: 0x000C0000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE, + }, { + name: "altkernel", + size: 0x000C0000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE, + }, { + name: "root", + size: 0x00400000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE, + }, { + name: "free1", + size: 0x00300000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE, + }, { + name: "free2", + size: 0x00300000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE, + }, { + name: "free3", + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE, + } +}; #endif + #ifdef CONFIG_SA1100_FREEBIRD -static unsigned long freebird_max_flash_size = 0x02000000; static struct mtd_partition freebird_partitions[] = { #if CONFIG_SA1100_FREEBIRD_NEW - { - name: "firmware", - size: 0x00040000, - offset: 0, - mask_flags: MTD_WRITEABLE /* force read-only */ - },{ - name: "kernel", - size: 0x00080000, - offset: 0x40000 - },{ - name: "params", - size: 0x00040000, - offset: 0xC0000 - },{ - name: "initrd", - size: 0x00100000, - offset: 0x00100000 - },{ - name: "root cramfs", - size: 0x00300000, - offset: 0x00200000 - },{ - name: "usr cramfs", - size: 0x00C00000, - offset: 0x00500000 - },{ - name: "local", - offset: 0x01100000, - size: MTDPART_SIZ_FULL + { + name: "firmware", + size: 0x00040000, + offset: 0, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "kernel", + size: 0x00080000, + offset: 0x00040000, + }, { + name: "params", + size: 0x00040000, + offset: 0x000C0000, + }, { + name: "initrd", + size: 0x00100000, + offset: 0x00100000, + }, { + name: "root cramfs", + size: 0x00300000, + offset: 0x00200000, + }, { + name: "usr cramfs", + size: 0x00C00000, + offset: 0x00500000, + }, { + name: "local", + size: MTDPART_SIZ_FULL, + offset: 0x01100000, } #else - { offset: 0, size: 0x00040000, }, - { offset: MTDPART_OFS_APPEND, size: 0x000c0000, }, - { offset: MTDPART_OFS_APPEND, size: 0x00400000, }, - { offset: MTDPART_OFS_APPEND, size: MTDPART_SIZ_FULL } + { + size: 0x00040000, + offset: 0, + }, { + size: 0x000c0000, + offset: MTDPART_OFS_APPEND, + }, { + size: 0x00400000, + offset: MTDPART_OFS_APPEND, + }, { + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + } #endif - }; +}; #endif - - -#ifdef CONFIG_SA1100_CERF -static unsigned long cerf_max_flash_size = 0x01000000; -static struct mtd_partition cerf_partitions[] = { - { offset: 0, size: 0x00800000 }, - { offset: MTDPART_OFS_APPEND, size: 0x00800000 } +#ifdef CONFIG_SA1100_FRODO +/* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */ +static struct mtd_partition frodo_partitions[] = +{ + { + name: "bootloader", + size: 0x00040000, + offset: 0x00000000, + mask_flags: MTD_WRITEABLE + }, { + name: "bootloader params", + size: 0x00040000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE + }, { + name: "kernel", + size: 0x00100000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE + }, { + name: "ramdisk", + size: 0x00400000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE + }, { + name: "file system", + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND + } }; - #endif #ifdef CONFIG_SA1100_GRAPHICSCLIENT - -static unsigned long graphicsclient_max_flash_size = 0x01000000; static struct mtd_partition graphicsclient_partitions[] = { - { - name: "zImage", - offset: 0, - size: 0x100000 - }, - { - name: "ramdisk.gz", - offset: MTDPART_OFS_APPEND, - size: 0x300000 - }, - { - name: "User FS", - offset: MTDPART_OFS_APPEND, - size: MTDPART_SIZ_FULL + { + name: "zImage", + size: 0x100000, + offset: 0, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "ramdisk.gz", + size: 0x300000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "User FS", + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, } }; - #endif #ifdef CONFIG_SA1100_GRAPHICSMASTER - -static unsigned long graphicsmaster_max_flash_size = 0x01000000; static struct mtd_partition graphicsmaster_partitions[] = { - { - name: "zImage", - offset: 0, - size: 0x100000 + { + name: "zImage", + size: 0x100000, + offset: 0, + mask_flags: MTD_WRITEABLE, /* force read-only */ }, - { - name: "ramdisk.gz", - offset: MTDPART_OFS_APPEND, - size: 0x300000 + { + name: "ramdisk.gz", + size: 0x300000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE, /* force read-only */ }, - { - name: "User FS", - offset: MTDPART_OFS_APPEND, - size: MTDPART_SIZ_FULL + { + name: "User FS", + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, } }; +#endif +#ifdef CONFIG_SA1100_H3XXX +static struct mtd_partition h3xxx_partitions[] = { + { + name: "H3XXX boot firmware", + size: 0x00040000, + offset: 0, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { +#ifdef CONFIG_MTD_2PARTS_IPAQ + name: "H3XXX root jffs2", + size: MTDPART_SIZ_FULL, + offset: 0x00040000, +#else + name: "H3XXX kernel", + size: 0x00080000, + offset: 0x00040000, + }, { + name: "H3XXX params", + size: 0x00040000, + offset: 0x000C0000, + }, { +#ifdef CONFIG_JFFS2_FS + name: "H3XXX root jffs2", + size: MTDPART_SIZ_FULL, + offset: 0x00100000, +#else + name: "H3XXX initrd", + size: 0x00100000, + offset: 0x00100000, + }, { + name: "H3XXX root cramfs", + size: 0x00300000, + offset: 0x00200000, + }, { + name: "H3XXX usr cramfs", + size: 0x00800000, + offset: 0x00500000, + }, { + name: "H3XXX usr local", + size: MTDPART_SIZ_FULL, + offset: 0x00d00000, +#endif #endif + } +}; -#ifdef CONFIG_SA1100_PANGOLIN +static void h3xxx_set_vpp(struct map_info *map, int vpp) +{ + assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, vpp); +} +#else +#define h3xxx_set_vpp NULL +#endif -static unsigned long pangolin_max_flash_size = 0x04000000; -static struct mtd_partition pangolin_partitions[] = { - { - name: "boot firmware", - offset: 0x00000000, - size: 0x00080000, - mask_flags: MTD_WRITEABLE, /* force read-only */ - }, - { - name: "kernel", - offset: 0x00080000, - size: 0x00100000, - }, +#ifdef CONFIG_SA1100_HUW_WEBPANEL +static struct mtd_partition huw_webpanel_partitions[] = { { - name: "initrd", - offset: 0x00180000, - size: 0x00280000, - }, + name: "Loader", + size: 0x00040000, + offset: 0, + }, { + name: "Sector 1", + size: 0x00040000, + offset: MTDPART_OFS_APPEND, + }, { + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + } +}; +#endif + +#ifdef CONFIG_SA1100_JORNADA720 +static struct mtd_partition jornada720_partitions[] = { { - name: "initrd-test", - offset: 0x00400000, - size: 0x03C00000, + name: "JORNADA720 boot firmware", + size: 0x00040000, + offset: 0, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "JORNADA720 kernel", + size: 0x000c0000, + offset: 0x00040000, + }, { + name: "JORNADA720 params", + size: 0x00040000, + offset: 0x00100000, + }, { + name: "JORNADA720 initrd", + size: 0x00100000, + offset: 0x00140000, + }, { + name: "JORNADA720 root cramfs", + size: 0x00300000, + offset: 0x00240000, + }, { + name: "JORNADA720 usr cramfs", + size: 0x00800000, + offset: 0x00540000, + }, { + name: "JORNADA720 usr local", + size: 0 /* will expand to the end of the flash */ + offset: 0x00d00000, } }; +static void jornada720_set_vpp(int vpp) +{ + if (vpp) + PPSR |= 0x80; + else + PPSR &= ~0x80; + PPDR |= 0x80; +} +#else +#define jornada720_set_vpp NULL #endif -#ifdef CONFIG_SA1100_YOPY +#ifdef CONFIG_SA1100_PANGOLIN +static struct mtd_partition pangolin_partitions[] = { + { + name: "boot firmware", + size: 0x00080000, + offset: 0x00000000, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "kernel", + size: 0x00100000, + offset: 0x00080000, + }, { + name: "initrd", + size: 0x00280000, + offset: 0x00180000, + }, { + name: "initrd-test", + size: 0x03C00000, + offset: 0x00400000, + } +}; +#endif -static unsigned long yopy_max_flash_size = 0x08000000; -static struct mtd_partition yopy_partitions[] = { +#ifdef CONFIG_SA1100_PT_SYSTEM3 +/* erase size is 0x40000 == 256k partitions have to have this boundary */ +static struct mtd_partition system3_partitions[] = { { - name: "boot firmware", - offset: 0x00000000, - size: 0x00040000, - mask_flags: MTD_WRITEABLE, /* force read-only */ + name: "BLOB", + size: 0x00040000, + offset: 0x00000000, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "config", + size: 0x00040000, + offset: MTDPART_OFS_APPEND, + }, { + name: "kernel", + size: 0x00100000, + offset: MTDPART_OFS_APPEND, + }, { + name: "root", + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + } +}; +#endif + +#ifdef CONFIG_SA1100_SHANNON +static struct mtd_partition shannon_partitions[] = { + { + name: "BLOB boot loader", + offset: 0, + size: 0x20000 }, { name: "kernel", - offset: 0x00080000, - size: 0x00080000, + offset: MTDPART_OFS_APPEND, + size: 0xe0000 }, - { + { name: "initrd", - offset: 0x00100000, - size: 0x00300000, - }, - { - name: "root", - offset: 0x00400000, - size: 0x01000000, - }, + offset: MTDPART_OFS_APPEND, + size: MTDPART_SIZ_FULL + } }; #endif -#ifdef CONFIG_SA1100_JORNADA720 - -static unsigned long jornada720_max_flash_size = 0x02000000; -static struct mtd_partition jornada720_partitions[] = { +#ifdef CONFIG_SA1100_SHERMAN +static struct mtd_partition sherman_partitions[] = { { - name: "JORNADA720 boot firmware", - size: 0x00040000, - offset: 0, - mask_flags: MTD_WRITEABLE /* force read-only */ - },{ - name: "JORNADA720 kernel", - size: 0x000c0000, - offset: 0x40000 - },{ - name: "JORNADA720 params", - size: 0x00040000, - offset: 0x100000 - },{ - name: "JORNADA720 initrd", - size: 0x00100000, - offset: 0x00140000 - },{ - name: "JORNADA720 root cramfs", - size: 0x00300000, - offset: 0x00240000 - },{ - name: "JORNADA720 usr cramfs", - size: 0x00800000, - offset: 0x00540000 - },{ - name: "JORNADA720 usr local", - offset: 0x00d00000, - size: 0 /* will expand to the end of the flash */ + size: 0x50000, + offset: 0, + }, { + size: 0x70000, + offset: MTDPART_OFS_APPEND, + }, { + size: 0x600000, + offset: MTDPART_OFS_APPEND, + }, { + size: 0xA0000, + offset: MTDPART_OFS_APPEND, } }; #endif -#ifdef CONFIG_SA1100_SHERMAN - -static unsigned long sherman_max_flash_size = 0x02000000; -static struct mtd_partition sherman_partitions[] = { - { offset: 0, size: 0x50000 }, - { offset: MTDPART_OFS_APPEND, size: 0x70000 }, - { offset: MTDPART_OFS_APPEND, size: 0x600000 }, - { offset: MTDPART_OFS_APPEND, size: 0xA0000 } -}; - +#ifdef CONFIG_SA1100_SIMPAD +static struct mtd_partition simpad_partitions[] = { + { + name: "SIMpad boot firmware", + size: 0x00080000, + offset: 0, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "SIMpad kernel", + size: 0x00100000, + offset: 0x00080000, + }, { +#ifdef CONFIG_JFFS2_FS + name: "SIMpad root jffs2", + size: MTDPART_SIZ_FULL, + offset: 0x00180000, +#else + name: "SIMpad initrd", + size: 0x00300000, + offset: 0x00180000, + }, { + name: "SIMpad root cramfs", + size: 0x00300000, + offset: 0x00480000, + }, { + name: "SIMpad usr cramfs", + size: 0x005c0000, + offset: 0x00780000, + }, { + name: "SIMpad usr local", + size: MTDPART_SIZ_FULL, + offset: 0x00d40000, #endif + } +}; +#endif /* CONFIG_SA1100_SIMPAD */ #ifdef CONFIG_SA1100_STORK - -static unsigned long stork_max_flash_size = 0x02000000; static struct mtd_partition stork_partitions[] = { { - name: "STORK boot firmware", - size: 0x00040000, - offset: 0, - mask_flags: MTD_WRITEABLE /* force read-only */ - },{ - name: "STORK params", - size: 0x00040000, - offset: 0x40000 - },{ - name: "STORK kernel", - size: 0x00100000, - offset: 0x80000 - },{ + name: "STORK boot firmware", + size: 0x00040000, + offset: 0, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "STORK params", + size: 0x00040000, + offset: 0x00040000, + }, { + name: "STORK kernel", + size: 0x00100000, + offset: 0x00080000, + }, { #ifdef CONFIG_JFFS2_FS - name: "STORK root jffs2", - offset: 0x00180000, - size: MTDPART_SIZ_FULL + name: "STORK root jffs2", + offset: 0x00180000, + size: MTDPART_SIZ_FULL, #else - name: "STORK initrd", - size: 0x00100000, - offset: 0x00180000 - },{ - name: "STORK root cramfs", - size: 0x00300000, - offset: 0x00280000 - },{ - name: "STORK usr cramfs", - size: 0x00800000, - offset: 0x00580000 - },{ - name: "STORK usr local", - offset: 0x00d80000, - size: MTDPART_SIZ_FULL + name: "STORK initrd", + size: 0x00100000, + offset: 0x00180000, + }, { + name: "STORK root cramfs", + size: 0x00300000, + offset: 0x00280000, + }, { + name: "STORK usr cramfs", + size: 0x00800000, + offset: 0x00580000, + }, { + name: "STORK usr local", + offset: 0x00d80000, + size: MTDPART_SIZ_FULL, #endif } }; - #endif -#define NB_OF(x) (sizeof(x)/sizeof(x[0])) - - -extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); -extern int parse_bootldr_partitions(struct mtd_info *master, struct mtd_partition **pparts); +#ifdef CONFIG_SA1100_TRIZEPS +static struct mtd_partition trizeps_partitions[] = { + { + name: "Bootloader & the kernel", + size: 0x00200000, + offset: 0, + }, { + name: "Data", + size: 0x00400000, + offset: MTDPART_OFS_APPEND, + }, { + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + } +}; +#endif -static struct mtd_partition *parsed_parts; -static struct mtd_info *mymtd; +#ifdef CONFIG_SA1100_YOPY +static struct mtd_partition yopy_partitions[] = { + { + name: "boot firmware", + size: 0x00040000, + offset: 0x00000000, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "kernel", + size: 0x00080000, + offset: 0x00080000, + }, { + name: "initrd", + size: 0x00300000, + offset: 0x00100000, + }, { + name: "root", + size: 0x01000000, + offset: 0x00400000, + } +}; +#endif -int __init sa1100_mtd_init(void) +static int __init sa1100_static_partitions(struct mtd_partition **parts) { - struct mtd_partition *parts; int nb_parts = 0; - int parsed_nr_parts = 0; - char *part_type; - - /* Default flash buswidth */ - sa1100_map.buswidth = (MSC0 & MSC_RBW) ? 2 : 4; - /* - * Static partition definition selection - */ - part_type = "static"; +#ifdef CONFIG_SA1100_ADSBITSY + if (machine_is_adsbitsy()) { + *parts = adsbitsy_partitions; + nb_parts = ARRAY_SIZE(adsbitsy_partitions); + } +#endif #ifdef CONFIG_SA1100_ASSABET if (machine_is_assabet()) { - parts = assabet_partitions; - nb_parts = NB_OF(assabet_partitions); - sa1100_map.size = assabet_max_flash_size; + *parts = assabet_partitions; + nb_parts = ARRAY_SIZE(assabet_partitions); } #endif - -#ifdef CONFIG_SA1100_HUW_WEBPANEL - if (machine_is_huw_webpanel()) { - parts = huw_webpanel_partitions; - nb_parts = NB_OF(huw_webpanel_partitions); - sa1100_map.size = huw_webpanel_max_flash_size; +#ifdef CONFIG_SA1100_BADGE4 + if (machine_is_badge4()) { + *parts = badge4_partitions; + nb_parts = ARRAY_SIZE(badge4_partitions); } #endif - -#ifdef CONFIG_SA1100_H3600 - if (machine_is_h3600()) { - parts = h3600_partitions; - nb_parts = NB_OF(h3600_partitions); - sa1100_map.size = h3600_max_flash_size; - sa1100_map.set_vpp = h3600_set_vpp; +#ifdef CONFIG_SA1100_CERF + if (machine_is_cerf()) { + *parts = cerf_partitions; + nb_parts = ARRAY_SIZE(cerf_partitions); + } +#endif +#ifdef CONFIG_SA1100_CONSUS + if (machine_is_consus()) { + *parts = consus_partitions; + nb_parts = ARRAY_SIZE(consus_partitions); + } +#endif +#ifdef CONFIG_SA1100_FLEXANET + if (machine_is_flexanet()) { + *parts = flexanet_partitions; + nb_parts = ARRAY_SIZE(flexanet_partitions); } #endif #ifdef CONFIG_SA1100_FREEBIRD if (machine_is_freebird()) { - parts = freebird_partitions; - nb_parts = NB_OF(freebird_partitions); - sa1100_map.size = freebird_max_flash_size; + *parts = freebird_partitions; + nb_parts = ARRAY_SIZE(freebird_partitions); } #endif -#ifdef CONFIG_SA1100_CERF - if (machine_is_cerf()) { - parts = cerf_partitions; - nb_parts = NB_OF(cerf_partitions); - sa1100_map.size = cerf_max_flash_size; +#ifdef CONFIG_SA1100_FRODO + if (machine_is_frodo()) { + *parts = frodo_partitions; + nb_parts = ARRAY_SIZE(frodo_partitions); } -#endif +#endif #ifdef CONFIG_SA1100_GRAPHICSCLIENT if (machine_is_graphicsclient()) { - parts = graphicsclient_partitions; - nb_parts = NB_OF(graphicsclient_partitions); - sa1100_map.size = graphicsclient_max_flash_size; - sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4; + *parts = graphicsclient_partitions; + nb_parts = ARRAY_SIZE(graphicsclient_partitions); } #endif #ifdef CONFIG_SA1100_GRAPHICSMASTER if (machine_is_graphicsmaster()) { - parts = graphicsmaster_partitions; - nb_parts = NB_OF(graphicsmaster_partitions); - sa1100_map.size = graphicsmaster_max_flash_size; - sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4; + *parts = graphicsmaster_partitions; + nb_parts = ARRAY_SIZE(graphicsmaster_partitions); } #endif -#ifdef CONFIG_SA1100_PANGOLIN - if (machine_is_pangolin()) { - parts = pangolin_partitions; - nb_parts = NB_OF(pangolin_partitions); - sa1100_map.size = pangolin_max_flash_size; +#ifdef CONFIG_SA1100_H3XXX + if (machine_is_h3xxx()) { + *parts = h3xxx_partitions; + nb_parts = ARRAY_SIZE(h3xxx_partitions); + } +#endif +#ifdef CONFIG_SA1100_HUW_WEBPANEL + if (machine_is_huw_webpanel()) { + *parts = huw_webpanel_partitions; + nb_parts = ARRAY_SIZE(huw_webpanel_partitions); } #endif #ifdef CONFIG_SA1100_JORNADA720 if (machine_is_jornada720()) { - parts = jornada720_partitions; - nb_parts = NB_OF(jornada720_partitions); - sa1100_map.size = jornada720_max_flash_size; - sa1100_map.set_vpp = jornada720_set_vpp; + *parts = jornada720_partitions; + nb_parts = ARRAY_SIZE(jornada720_partitions); } #endif -#ifdef CONFIG_SA1100_YOPY - if (machine_is_yopy()) { - parts = yopy_partitions; - nb_parts = NB_OF(yopy_partitions); - sa1100_map.size = yopy_max_flash_size; +#ifdef CONFIG_SA1100_PANGOLIN + if (machine_is_pangolin()) { + *parts = pangolin_partitions; + nb_parts = ARRAY_SIZE(pangolin_partitions); + } +#endif +#ifdef CONFIG_SA1100_PT_SYSTEM3 + if (machine_is_pt_system3()) { + *parts = system3_partitions; + nb_parts = ARRAY_SIZE(system3_partitions); + } +#endif +#ifdef CONFIG_SA1100_SHANNON + if (machine_is_shannon()) { + *parts = shannon_partitions; + nb_parts = ARRAY_SIZE(shannon_partitions); } #endif #ifdef CONFIG_SA1100_SHERMAN if (machine_is_sherman()) { - parts = sherman_partitions; - nb_parts = NB_OF(sherman_partitions); - sa1100_map.size = sherman_max_flash_size; + *parts = sherman_partitions; + nb_parts = ARRAY_SIZE(sherman_partitions); } #endif -#ifdef CONFIG_SA1100_FLEXANET - if (machine_is_flexanet()) { - parts = flexanet_partitions; - nb_parts = NB_OF(flexanet_partitions); - sa1100_map.size = flexanet_max_flash_size; +#ifdef CONFIG_SA1100_SIMPAD + if (machine_is_simpad()) { + *parts = simpad_partitions; + nb_parts = ARRAY_SIZE(simpad_partitions); } #endif #ifdef CONFIG_SA1100_STORK if (machine_is_stork()) { - parts = stork_partitions; - nb_parts = NB_OF(stork_partitions); - sa1100_map.size = stork_max_flash_size; + *parts = stork_partitions; + nb_parts = ARRAY_SIZE(stork_partitions); + } +#endif +#ifdef CONFIG_SA1100_TRIZEPS + if (machine_is_trizeps()) { + *parts = trizeps_partitions; + nb_parts = ARRAY_SIZE(trizeps_parititons); + } +#endif +#ifdef CONFIG_SA1100_YOPY + if (machine_is_yopy()) { + *parts = yopy_partitions; + nb_parts = ARRAY_SIZE(yopy_partitions); } #endif + return nb_parts; +} +#endif + +struct sa_info { + unsigned long base; + unsigned long size; + int width; + void *vbase; + struct map_info *map; + struct mtd_info *mtd; + struct resource *res; +}; + +#define NR_SUBMTD 4 + +static struct sa_info info[NR_SUBMTD]; + +static int __init sa1100_setup_mtd(struct sa_info *sa, int nr, struct mtd_info **rmtd) +{ + struct mtd_info *subdev[nr]; + struct map_info *maps; + int i, found = 0, ret = 0; + /* - * Now let's probe for the actual flash. Do it here since - * specific machine settings might have been set above. + * Allocate the map_info structs in one go. */ - printk(KERN_NOTICE "SA1100 flash: probing %d-bit flash bus\n", sa1100_map.buswidth*8); - mymtd = do_map_probe("cfi_probe", &sa1100_map); - if (!mymtd) - return -ENXIO; - mymtd->module = THIS_MODULE; + maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL); + if (!maps) + return -ENOMEM; /* - * Dynamic partition selection stuff (might override the static ones) + * Claim and then map the memory regions. */ -#ifdef CONFIG_MTD_REDBOOT_PARTS - if (parsed_nr_parts == 0) { - int ret = parse_redboot_partitions(mymtd, &parsed_parts); - - if (ret > 0) { - part_type = "RedBoot"; - parsed_nr_parts = ret; + for (i = 0; i < nr; i++) { + if (sa[i].base == (unsigned long)-1) + break; + + sa[i].res = request_mem_region(sa[i].base, sa[i].size, "sa1100 flash"); + if (!sa[i].res) { + ret = -EBUSY; + break; + } + + sa[i].map = maps + i; + memcpy(sa[i].map, &sa1100_map, sizeof(struct map_info)); + + sa[i].vbase = ioremap(sa[i].base, sa[i].size); + if (!sa[i].vbase) { + ret = -ENOMEM; + break; } + + sa[i].map->map_priv_1 = (unsigned long)sa[i].vbase; + sa[i].map->buswidth = sa[i].width; + sa[i].map->size = sa[i].size; + + /* + * Now let's probe for the actual flash. Do it here since + * specific machine settings might have been set above. + */ + sa[i].mtd = do_map_probe("cfi_probe", sa[i].map); + if (sa[i].mtd == NULL) { + ret = -ENXIO; + break; + } + sa[i].mtd->module = THIS_MODULE; + subdev[i] = sa[i].mtd; + + printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, " + "%d-bit\n", sa[i].base, sa[i].mtd->size >> 20, + sa[i].width * 8); + found += 1; } + + /* + * ENXIO is special. It means we didn't find a chip when + * we probed. We need to tear down the mapping, free the + * resource and mark it as such. + */ + if (ret == -ENXIO) { + iounmap(sa[i].vbase); + sa[i].vbase = NULL; + release_resource(sa[i].res); + sa[i].res = NULL; + } + + /* + * If we found one device, don't bother with concat support. + * If we found multiple devices, use concat if we have it + * available, otherwise fail. + */ + if (ret == 0 || ret == -ENXIO) { + if (found == 1) { + *rmtd = subdev[0]; + ret = 0; + } else if (found > 1) { + /* + * We detected multiple devices. Concatenate + * them together. + */ +#ifdef CONFIG_MTD_CONCAT + *rmtd = mtd_concat_create(subdev, found, + "sa1100 flash"); + if (*rmtd == NULL) + ret = -ENXIO; +#else + printk(KERN_ERR "SA1100 flash: multiple devices " + "found but MTD concat support disabled.\n"); + ret = -ENXIO; #endif -#ifdef CONFIG_MTD_BOOTLDR_PARTS - if (parsed_nr_parts == 0) { - int ret = parse_bootldr_partitions(mymtd, &parsed_parts); - if (ret > 0) { - part_type = "Compaq bootldr"; - parsed_nr_parts = ret; } } -#endif - if (parsed_nr_parts > 0) { - parts = parsed_parts; - nb_parts = parsed_nr_parts; + /* + * If we failed, clean up. + */ + if (ret) { + do { + if (sa[i].mtd) + map_destroy(sa[i].mtd); + if (sa[i].vbase) + iounmap(sa[i].vbase); + if (sa[i].res) + release_resource(sa[i].res); + } while (i--); + + kfree(maps); } - if (nb_parts == 0) { - printk(KERN_NOTICE "SA1100 flash: no partition info available, registering whole flash at once\n"); - add_mtd_device(mymtd); + return ret; +} + +static void __exit sa1100_destroy_mtd(struct sa_info *sa, struct mtd_info *mtd) +{ + int i; + + del_mtd_partitions(mtd); + + if (mtd != sa[0].mtd) + mtd_concat_destroy(mtd); + + for (i = NR_SUBMTD; i >= 0; i--) { + if (sa[i].mtd) + map_destroy(sa[i].mtd); + if (sa[i].vbase) + iounmap(sa[i].vbase); + if (sa[i].res) + release_resource(sa[i].res); + } + kfree(sa[0].map); +} + +static int __init sa1100_locate_flash(void) +{ + int i, nr = -ENODEV; + + if (machine_is_adsbitsy()) { + info[0].base = SA1100_CS1_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_assabet()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + info[1].base = SA1100_CS1_PHYS; /* neponset */ + info[1].size = SZ_32M; + nr = 2; + } + if (machine_is_badge4()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_4M; + nr = 1; + } + if (machine_is_cerf()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_consus()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_flexanet()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_freebird()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_frodo()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_graphicsclient()) { + info[0].base = SA1100_CS1_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_graphicsmaster()) { + info[0].base = SA1100_CS1_PHYS; + info[0].size = SZ_16M; + nr = 1; + } + if (machine_is_h3xxx()) { + sa1100_map.set_vpp = h3xxx_set_vpp; + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_huw_webpanel()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_16M; + nr = 1; + } + if (machine_is_itsy()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_jornada720()) { + sa1100_map.set_vpp = jornada720_set_vpp; + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_nanoengine()) { + info[0].base = SA1100_CS0_PHYS; + info[1].size = SZ_32M; + nr = 1; + } + if (machine_is_pangolin()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_64M; + nr = 1; + } + if (machine_is_pfs168()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_pleb()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_4M; + info[1].base = SA1100_CS1_PHYS; + info[1].size = SZ_4M; + nr = 2; + } + if (machine_is_pt_system3()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_16M; + nr = 1; + } + if (machine_is_shannon()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_4M; + nr = 1; + } + if (machine_is_sherman()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_simpad()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_stork()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } + if (machine_is_trizeps()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_16M; + nr = 1; + } + if (machine_is_victor()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_2M; + nr = 1; + } + if (machine_is_yopy()) { + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_64M; + info[1].base = SA1100_CS1_PHYS; + info[1].size = SZ_64M; + nr = 2; + } + + if (nr < 0) + return nr; + + /* + * Retrieve the buswidth from the MSC registers. + * We currently only implement CS0 and CS1 here. + */ + for (i = 0; i < nr; i++) { + switch (info[i].base) { + default: + printk(KERN_WARNING "SA1100 flash: unknown base address " + "0x%08lx, assuming CS0\n", info[i].base); + case SA1100_CS0_PHYS: + info[i].width = (MSC0 & MSC_RBW) ? 2 : 4; + break; + + case SA1100_CS1_PHYS: + info[i].width = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4; + break; + } + } + + return nr; +} + +extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *); + +static struct mtd_partition *parsed_parts; + +static void __init sa1100_locate_partitions(struct mtd_info *mtd) +{ + const char *part_type = NULL; + int nr_parts = 0; + + do { + /* + * Partition selection stuff. + */ +#ifdef CONFIG_MTD_CMDLINE_PARTS + nr_parts = parse_cmdline_partitions(mtd, &parsed_parts, "sa1100"); + if (nr_parts > 0) { + part_type = "command line"; + break; + } +#endif +#ifdef CONFIG_MTD_REDBOOT_PARTS + nr_parts = parse_redboot_partitions(mtd, &parsed_parts); + if (nr_parts > 0) { + part_type = "RedBoot"; + break; + } +#endif +#ifdef CONFIG_MTD_SA1100_STATICMAP + nr_parts = sa1100_static_partitions(&parsed_parts); + if (nr_parts > 0) { + part_type = "static"; + break; + } +#endif + } while (0); + + if (nr_parts == 0) { + printk(KERN_NOTICE "SA1100 flash: no partition info " + "available, registering whole flash\n"); + add_mtd_device(mtd); } else { - printk(KERN_NOTICE "Using %s partition definition\n", part_type); - add_mtd_partitions(mymtd, parts, nb_parts); + printk(KERN_NOTICE "SA1100 flash: using %s partition " + "definition\n", part_type); + add_mtd_partitions(mtd, parsed_parts, nr_parts); } - return 0; + + /* Always succeeds. */ +} + +static void __exit sa1100_destroy_partitions(void) +{ + if (parsed_parts) + kfree(parsed_parts); +} + +static struct mtd_info *mymtd; + +static int __init sa1100_mtd_init(void) +{ + int ret; + int nr; + + nr = sa1100_locate_flash(); + if (nr < 0) + return nr; + + ret = sa1100_setup_mtd(info, nr, &mymtd); + if (ret == 0) + sa1100_locate_partitions(mymtd); + + return ret; } static void __exit sa1100_mtd_cleanup(void) { - if (mymtd) { - del_mtd_partitions(mymtd); - map_destroy(mymtd); - if (parsed_parts) - kfree(parsed_parts); - } + sa1100_destroy_mtd(info, mymtd); + sa1100_destroy_partitions(); } module_init(sa1100_mtd_init); diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c new file mode 100644 index 000000000000..4c16c0e43e0f --- /dev/null +++ b/drivers/mtd/mtdconcat.c @@ -0,0 +1,675 @@ +/* + * MTD device concatenation layer + * + * (C) 2002 Robert Kaiser + * + * This code is GPL + * + * $Id: mtdconcat.c,v 1.3 2002/05/21 21:04:25 dwmw2 Exp $ + */ + +#include +#include +#include +#include + +#include +#include + +/* + * Our storage structure: + * Subdev points to an array of pointers to struct mtd_info objects + * which is allocated along with this structure + * + */ +struct mtd_concat { + struct mtd_info mtd; + int num_subdev; + struct mtd_info **subdev; +}; + +/* + * how to calculate the size required for the above structure, + * including the pointer array subdev points to: + */ +#define SIZEOF_STRUCT_MTD_CONCAT(num_subdev) \ + ((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *))) + + +/* + * Given a pointer to the MTD object in the mtd_concat structure, + * we can retrieve the pointer to that structure with this macro. + */ +#define CONCAT(x) ((struct mtd_concat *)(x)) + + +/* + * MTD methods which look up the relevant subdevice, translate the + * effective address and pass through to the subdevice. + */ + +static int concat_read (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct mtd_concat *concat = CONCAT(mtd); + int err = -EINVAL; + int i; + + *retlen = 0; + + for(i = 0; i < concat->num_subdev; i++) + { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + + if (from >= subdev->size) + { + size = 0; + from -= subdev->size; + } + else + { + if (from + len > subdev->size) + size = subdev->size - from; + else + size = len; + + err = subdev->read(subdev, from, size, &retsize, buf); + + if(err) + break; + + *retlen += retsize; + len -= size; + if(len == 0) + break; + + err = -EINVAL; + buf += size; + from = 0; + } + } + return err; +} + +static int concat_write (struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct mtd_concat *concat = CONCAT(mtd); + int err = -EINVAL; + int i; + + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + + *retlen = 0; + + for(i = 0; i < concat->num_subdev; i++) + { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + + if (to >= subdev->size) + { + size = 0; + to -= subdev->size; + } + else + { + if (to + len > subdev->size) + size = subdev->size - to; + else + size = len; + + if (!(subdev->flags & MTD_WRITEABLE)) + err = -EROFS; + else + err = subdev->write(subdev, to, size, &retsize, buf); + + if(err) + break; + + *retlen += retsize; + len -= size; + if(len == 0) + break; + + err = -EINVAL; + buf += size; + to = 0; + } + } + return err; +} + +static void concat_erase_callback (struct erase_info *instr) +{ + wake_up((wait_queue_head_t *)instr->priv); +} + +static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) +{ + int err; + wait_queue_head_t waitq; + DECLARE_WAITQUEUE(wait, current); + + /* + * This code was stol^H^H^H^Hinspired by mtdchar.c + */ + init_waitqueue_head(&waitq); + + erase->mtd = mtd; + erase->callback = concat_erase_callback; + erase->priv = (unsigned long)&waitq; + + /* + * FIXME: Allow INTERRUPTIBLE. Which means + * not having the wait_queue head on the stack. + */ + err = mtd->erase(mtd, erase); + if (!err) + { + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&waitq, &wait); + if (erase->state != MTD_ERASE_DONE && erase->state != MTD_ERASE_FAILED) + schedule(); + remove_wait_queue(&waitq, &wait); + set_current_state(TASK_RUNNING); + + err = (erase->state == MTD_ERASE_FAILED) ? -EIO : 0; + } + return err; +} + +static int concat_erase (struct mtd_info *mtd, struct erase_info *instr) +{ + struct mtd_concat *concat = CONCAT(mtd); + struct mtd_info *subdev; + int i, err; + u_int32_t length; + struct erase_info *erase; + + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + + if(instr->addr > concat->mtd.size) + return -EINVAL; + + if(instr->len + instr->addr > concat->mtd.size) + return -EINVAL; + + /* + * Check for proper erase block alignment of the to-be-erased area. + * It is easier to do this based on the super device's erase + * region info rather than looking at each particular sub-device + * in turn. + */ + if (!concat->mtd.numeraseregions) + { /* the easy case: device has uniform erase block size */ + if(instr->addr & (concat->mtd.erasesize - 1)) + return -EINVAL; + if(instr->len & (concat->mtd.erasesize - 1)) + return -EINVAL; + } + else + { /* device has variable erase size */ + struct mtd_erase_region_info *erase_regions = concat->mtd.eraseregions; + + /* + * Find the erase region where the to-be-erased area begins: + */ + for(i = 0; i < concat->mtd.numeraseregions && + instr->addr >= erase_regions[i].offset; i++) + ; + --i; + + /* + * Now erase_regions[i] is the region in which the + * to-be-erased area begins. Verify that the starting + * offset is aligned to this region's erase size: + */ + if (instr->addr & (erase_regions[i].erasesize-1)) + return -EINVAL; + + /* + * now find the erase region where the to-be-erased area ends: + */ + for(; i < concat->mtd.numeraseregions && + (instr->addr + instr->len) >= erase_regions[i].offset ; ++i) + ; + --i; + /* + * check if the ending offset is aligned to this region's erase size + */ + if ((instr->addr + instr->len) & (erase_regions[i].erasesize-1)) + return -EINVAL; + } + + /* make a local copy of instr to avoid modifying the caller's struct */ + erase = kmalloc(sizeof(struct erase_info),GFP_KERNEL); + + if (!erase) + return -ENOMEM; + + *erase = *instr; + length = instr->len; + + /* + * find the subdevice where the to-be-erased area begins, adjust + * starting offset to be relative to the subdevice start + */ + for(i = 0; i < concat->num_subdev; i++) + { + subdev = concat->subdev[i]; + if(subdev->size <= erase->addr) + erase->addr -= subdev->size; + else + break; + } + if(i >= concat->num_subdev) /* must never happen since size */ + BUG(); /* limit has been verified above */ + + /* now do the erase: */ + err = 0; + for(;length > 0; i++) /* loop for all subevices affected by this request */ + { + subdev = concat->subdev[i]; /* get current subdevice */ + + /* limit length to subdevice's size: */ + if(erase->addr + length > subdev->size) + erase->len = subdev->size - erase->addr; + else + erase->len = length; + + if (!(subdev->flags & MTD_WRITEABLE)) + { + err = -EROFS; + break; + } + length -= erase->len; + if ((err = concat_dev_erase(subdev, erase))) + { + if(err == -EINVAL) /* sanity check: must never happen since */ + BUG(); /* block alignment has been checked above */ + break; + } + /* + * erase->addr specifies the offset of the area to be + * erased *within the current subdevice*. It can be + * non-zero only the first time through this loop, i.e. + * for the first subdevice where blocks need to be erased. + * All the following erases must begin at the start of the + * current subdevice, i.e. at offset zero. + */ + erase->addr = 0; + } + kfree(erase); + if (err) + return err; + + instr->state = MTD_ERASE_DONE; + if (instr->callback) + instr->callback(instr); + return 0; +} + +static int concat_lock (struct mtd_info *mtd, loff_t ofs, size_t len) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, err = -EINVAL; + + if ((len + ofs) > mtd->size) + return -EINVAL; + + for(i = 0; i < concat->num_subdev; i++) + { + struct mtd_info *subdev = concat->subdev[i]; + size_t size; + + if (ofs >= subdev->size) + { + size = 0; + ofs -= subdev->size; + } + else + { + if (ofs + len > subdev->size) + size = subdev->size - ofs; + else + size = len; + + err = subdev->lock(subdev, ofs, size); + + if(err) + break; + + len -= size; + if(len == 0) + break; + + err = -EINVAL; + ofs = 0; + } + } + return err; +} + +static int concat_unlock (struct mtd_info *mtd, loff_t ofs, size_t len) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, err = 0; + + if ((len + ofs) > mtd->size) + return -EINVAL; + + for(i = 0; i < concat->num_subdev; i++) + { + struct mtd_info *subdev = concat->subdev[i]; + size_t size; + + if (ofs >= subdev->size) + { + size = 0; + ofs -= subdev->size; + } + else + { + if (ofs + len > subdev->size) + size = subdev->size - ofs; + else + size = len; + + err = subdev->unlock(subdev, ofs, size); + + if(err) + break; + + len -= size; + if(len == 0) + break; + + err = -EINVAL; + ofs = 0; + } + } + return err; +} + +static void concat_sync(struct mtd_info *mtd) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i; + + for(i = 0; i < concat->num_subdev; i++) + { + struct mtd_info *subdev = concat->subdev[i]; + subdev->sync(subdev); + } +} + +static int concat_suspend(struct mtd_info *mtd) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, rc = 0; + + for(i = 0; i < concat->num_subdev; i++) + { + struct mtd_info *subdev = concat->subdev[i]; + if((rc = subdev->suspend(subdev)) < 0) + return rc; + } + return rc; +} + +static void concat_resume(struct mtd_info *mtd) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i; + + for(i = 0; i < concat->num_subdev; i++) + { + struct mtd_info *subdev = concat->subdev[i]; + subdev->resume(subdev); + } +} + +/* + * This function constructs a virtual MTD device by concatenating + * num_devs MTD devices. A pointer to the new device object is + * stored to *new_dev upon success. This function does _not_ + * register any devices: this is the caller's responsibility. + */ +struct mtd_info *mtd_concat_create( + struct mtd_info *subdev[], /* subdevices to concatenate */ + int num_devs, /* number of subdevices */ + char *name) /* name for the new device */ +{ + int i; + size_t size; + struct mtd_concat *concat; + u_int32_t max_erasesize, curr_erasesize; + int num_erase_region; + + printk(KERN_NOTICE "Concatenating MTD devices:\n"); + for(i = 0; i < num_devs; i++) + printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name); + printk(KERN_NOTICE "into device \"%s\"\n", name); + + /* allocate the device structure */ + size = SIZEOF_STRUCT_MTD_CONCAT(num_devs); + concat = kmalloc (size, GFP_KERNEL); + if(!concat) + { + printk ("memory allocation error while creating concatenated device \"%s\"\n", + name); + return NULL; + } + memset(concat, 0, size); + concat->subdev = (struct mtd_info **)(concat + 1); + + /* + * Set up the new "super" device's MTD object structure, check for + * incompatibilites between the subdevices. + */ + concat->mtd.type = subdev[0]->type; + concat->mtd.flags = subdev[0]->flags; + concat->mtd.size = subdev[0]->size; + concat->mtd.erasesize = subdev[0]->erasesize; + concat->mtd.oobblock = subdev[0]->oobblock; + concat->mtd.oobsize = subdev[0]->oobsize; + concat->mtd.ecctype = subdev[0]->ecctype; + concat->mtd.eccsize = subdev[0]->eccsize; + + concat->subdev[0] = subdev[0]; + + for(i = 1; i < num_devs; i++) + { + if(concat->mtd.type != subdev[i]->type) + { + kfree(concat); + printk ("Incompatible device type on \"%s\"\n", subdev[i]->name); + return NULL; + } + if(concat->mtd.flags != subdev[i]->flags) + { /* + * Expect all flags except MTD_WRITEABLE to be equal on + * all subdevices. + */ + if((concat->mtd.flags ^ subdev[i]->flags) & ~MTD_WRITEABLE) + { + kfree(concat); + printk ("Incompatible device flags on \"%s\"\n", subdev[i]->name); + return NULL; + } + else /* if writeable attribute differs, make super device writeable */ + concat->mtd.flags |= subdev[i]->flags & MTD_WRITEABLE; + } + concat->mtd.size += subdev[i]->size; + if(concat->mtd.oobblock != subdev[i]->oobblock || + concat->mtd.oobsize != subdev[i]->oobsize || + concat->mtd.ecctype != subdev[i]->ecctype || + concat->mtd.eccsize != subdev[i]->eccsize) + { + kfree(concat); + printk ("Incompatible OOB or ECC data on \"%s\"\n", subdev[i]->name); + return NULL; + } + concat->subdev[i] = subdev[i]; + + } + + concat->num_subdev = num_devs; + concat->mtd.name = name; + + /* + * NOTE: for now, we do not provide any readv()/writev() methods + * because they are messy to implement and they are not + * used to a great extent anyway. + */ + concat->mtd.erase = concat_erase; + concat->mtd.read = concat_read; + concat->mtd.write = concat_write; + concat->mtd.sync = concat_sync; + concat->mtd.lock = concat_lock; + concat->mtd.unlock = concat_unlock; + concat->mtd.suspend = concat_suspend; + concat->mtd.resume = concat_resume; + + + /* + * Combine the erase block size info of the subdevices: + * + * first, walk the map of the new device and see how + * many changes in erase size we have + */ + max_erasesize = curr_erasesize = subdev[0]->erasesize; + num_erase_region = 1; + for(i = 0; i < num_devs; i++) + { + if(subdev[i]->numeraseregions == 0) + { /* current subdevice has uniform erase size */ + if(subdev[i]->erasesize != curr_erasesize) + { /* if it differs from the last subdevice's erase size, count it */ + ++num_erase_region; + curr_erasesize = subdev[i]->erasesize; + if(curr_erasesize > max_erasesize) + max_erasesize = curr_erasesize; + } + } + else + { /* current subdevice has variable erase size */ + int j; + for(j = 0; j < subdev[i]->numeraseregions; j++) + { /* walk the list of erase regions, count any changes */ + if(subdev[i]->eraseregions[j].erasesize != curr_erasesize) + { + ++num_erase_region; + curr_erasesize = subdev[i]->eraseregions[j].erasesize; + if(curr_erasesize > max_erasesize) + max_erasesize = curr_erasesize; + } + } + } + } + + if(num_erase_region == 1) + { /* + * All subdevices have the same uniform erase size. + * This is easy: + */ + concat->mtd.erasesize = curr_erasesize; + concat->mtd.numeraseregions = 0; + } + else + { /* + * erase block size varies across the subdevices: allocate + * space to store the data describing the variable erase regions + */ + struct mtd_erase_region_info *erase_region_p; + u_int32_t begin, position; + + concat->mtd.erasesize = max_erasesize; + concat->mtd.numeraseregions = num_erase_region; + concat->mtd.eraseregions = erase_region_p = kmalloc ( + num_erase_region * sizeof(struct mtd_erase_region_info), GFP_KERNEL); + if(!erase_region_p) + { + kfree(concat); + printk ("memory allocation error while creating erase region list" + " for device \"%s\"\n", name); + return NULL; + } + + /* + * walk the map of the new device once more and fill in + * in erase region info: + */ + curr_erasesize = subdev[0]->erasesize; + begin = position = 0; + for(i = 0; i < num_devs; i++) + { + if(subdev[i]->numeraseregions == 0) + { /* current subdevice has uniform erase size */ + if(subdev[i]->erasesize != curr_erasesize) + { /* + * fill in an mtd_erase_region_info structure for the area + * we have walked so far: + */ + erase_region_p->offset = begin; + erase_region_p->erasesize = curr_erasesize; + erase_region_p->numblocks = (position - begin) / curr_erasesize; + begin = position; + + curr_erasesize = subdev[i]->erasesize; + ++erase_region_p; + } + position += subdev[i]->size; + } + else + { /* current subdevice has variable erase size */ + int j; + for(j = 0; j < subdev[i]->numeraseregions; j++) + { /* walk the list of erase regions, count any changes */ + if(subdev[i]->eraseregions[j].erasesize != curr_erasesize) + { + erase_region_p->offset = begin; + erase_region_p->erasesize = curr_erasesize; + erase_region_p->numblocks = (position - begin) / curr_erasesize; + begin = position; + + curr_erasesize = subdev[i]->eraseregions[j].erasesize; + ++erase_region_p; + } + position += subdev[i]->eraseregions[j].numblocks * curr_erasesize; + } + } + } + /* Now write the final entry */ + erase_region_p->offset = begin; + erase_region_p->erasesize = curr_erasesize; + erase_region_p->numblocks = (position - begin) / curr_erasesize; + } + + return &concat->mtd; +} + +/* + * This function destroys an MTD object obtained from concat_mtd_devs() + */ + +void mtd_concat_destroy(struct mtd_info *mtd) +{ + struct mtd_concat *concat = CONCAT(mtd); + if(concat->mtd.numeraseregions) + kfree(concat->mtd.eraseregions); + kfree(concat); +} + + +EXPORT_SYMBOL(mtd_concat_create); +EXPORT_SYMBOL(mtd_concat_destroy); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Robert Kaiser "); +MODULE_DESCRIPTION("Generic support for concatenating of MTD devices"); diff --git a/include/linux/mtd/concat.h b/include/linux/mtd/concat.h new file mode 100644 index 000000000000..ed8dc6755219 --- /dev/null +++ b/include/linux/mtd/concat.h @@ -0,0 +1,23 @@ +/* + * MTD device concatenation layer definitions + * + * (C) 2002 Robert Kaiser + * + * This code is GPL + * + * $Id: concat.h,v 1.1 2002/03/08 16:34:36 rkaiser Exp $ + */ + +#ifndef MTD_CONCAT_H +#define MTD_CONCAT_H + + +struct mtd_info *mtd_concat_create( + struct mtd_info *subdev[], /* subdevices to concatenate */ + int num_devs, /* number of subdevices */ + char *name); /* name for the new device */ + +void mtd_concat_destroy(struct mtd_info *mtd); + +#endif + -- cgit v1.2.3 From 90df68e70b631886169c9287faebf2742f43484c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 14 Oct 2002 21:24:37 -0700 Subject: Block layer ioctl cleanups. Rename old "block_ioctl()" function: it's "scsi_cmd_ioctl()", as that is what the function does. Rename the whole file "scsi_ioctl.c" --- drivers/block/Makefile | 4 +- drivers/block/blkpg.c | 2 +- drivers/block/block_ioctl.c | 83 ----------------- drivers/block/scsi_ioctl.c | 215 ++++++++++++++++++++++++++++++++++++++++++++ drivers/ide/ide.c | 2 +- fs/block_dev.c | 41 +-------- include/linux/blkdev.h | 12 ++- 7 files changed, 233 insertions(+), 126 deletions(-) delete mode 100644 drivers/block/block_ioctl.c create mode 100644 drivers/block/scsi_ioctl.c (limited to 'include') diff --git a/drivers/block/Makefile b/drivers/block/Makefile index eff7ee947ea7..8457b1bfa13a 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -9,9 +9,9 @@ # export-objs := elevator.o ll_rw_blk.o loop.o genhd.o acsi.o \ - block_ioctl.o deadline-iosched.o + scsi_ioctl.o deadline-iosched.o -obj-y := elevator.o ll_rw_blk.o blkpg.o genhd.o block_ioctl.o deadline-iosched.o +obj-y := elevator.o ll_rw_blk.o blkpg.o genhd.o scsi_ioctl.o deadline-iosched.o obj-$(CONFIG_MAC_FLOPPY) += swim3.o obj-$(CONFIG_BLK_DEV_FD) += floppy.o diff --git a/drivers/block/blkpg.c b/drivers/block/blkpg.c index d5ba72a8ac86..7b55729fa29a 100644 --- a/drivers/block/blkpg.c +++ b/drivers/block/blkpg.c @@ -305,6 +305,6 @@ int blk_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg) return 0; default: - return -EINVAL; + return -ENOTTY; } } diff --git a/drivers/block/block_ioctl.c b/drivers/block/block_ioctl.c deleted file mode 100644 index edde76503d60..000000000000 --- a/drivers/block/block_ioctl.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2001 Jens Axboe - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public Licens - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111- - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -int blk_do_rq(request_queue_t *q, struct request *rq) -{ - DECLARE_COMPLETION(wait); - int err = 0; - - rq->flags |= REQ_NOMERGE; - rq->waiting = &wait; - elv_add_request(q, rq, 1); - generic_unplug_device(q); - wait_for_completion(&wait); - - /* - * for now, never retry anything - */ - if (rq->errors) - err = -EIO; - - return err; -} - -int block_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg) -{ - request_queue_t *q; - struct request *rq; - int close = 0, err; - - q = bdev_get_queue(bdev); - if (!q) - return -ENXIO; - - switch (cmd) { - case CDROMCLOSETRAY: - close = 1; - case CDROMEJECT: - rq = blk_get_request(q, WRITE, __GFP_WAIT); - rq->flags = REQ_BLOCK_PC; - memset(rq->cmd, 0, sizeof(rq->cmd)); - rq->cmd[0] = GPCMD_START_STOP_UNIT; - rq->cmd[4] = 0x02 + (close != 0); - err = blk_do_rq(q, rq); - blk_put_request(rq); - break; - default: - err = -ENOTTY; - } - - blk_put_queue(q); - return err; -} - -EXPORT_SYMBOL(block_ioctl); diff --git a/drivers/block/scsi_ioctl.c b/drivers/block/scsi_ioctl.c new file mode 100644 index 000000000000..c26646e578da --- /dev/null +++ b/drivers/block/scsi_ioctl.c @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2001 Jens Axboe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public Licens + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111- + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +int blk_do_rq(request_queue_t *q, struct request *rq) +{ + DECLARE_COMPLETION(wait); + int err = 0; + + rq->flags |= REQ_NOMERGE; + rq->waiting = &wait; + elv_add_request(q, rq, 1); + generic_unplug_device(q); + wait_for_completion(&wait); + + /* + * for now, never retry anything + */ + if (rq->errors) + err = -EIO; + + return err; +} + +#include + +static int sg_get_version(int *p) +{ + static int sg_version_num = 30527; + return put_user(sg_version_num, p); +} + +static int scsi_get_idlun(request_queue_t *q, int *p) +{ + return put_user(0, p); +} + +static int scsi_get_bus(request_queue_t *q, int *p) +{ + return put_user(0, p); +} + +static int sg_get_timeout(request_queue_t *q) +{ + return HZ; +} + +static int sg_set_timeout(request_queue_t *q, int *p) +{ + int timeout; + int error = get_user(timeout, p); + return error; +} + +static int reserved_size = 0; + +static int sg_get_reserved_size(request_queue_t *q, int *p) +{ + return put_user(reserved_size, p); +} + +static int sg_set_reserved_size(request_queue_t *q, int *p) +{ + int size; + int error = get_user(size, p); + if (!error) + reserved_size = size; + return error; +} + +static int sg_emulated_host(request_queue_t *q, int *p) +{ + return put_user(1, p); +} + +static int sg_io(request_queue_t *q, struct sg_io_hdr *uptr) +{ + int i, err; + struct sg_io_hdr hdr; + struct request *rq; + void *buffer; + + if (!access_ok(VERIFY_WRITE, uptr, sizeof(*uptr))) + return -EFAULT; + if (copy_from_user(&hdr, uptr, sizeof(*uptr))) + return -EFAULT; + + if ( hdr.cmd_len > sizeof(rq->cmd) ) + return -EINVAL; + + buffer = NULL; + if (hdr.dxfer_len) { + unsigned int bytes = (hdr.dxfer_len + 511) & ~511; + + switch (hdr.dxfer_direction) { + default: + return -EINVAL; + case SG_DXFER_TO_DEV: + case SG_DXFER_FROM_DEV: + case SG_DXFER_TO_FROM_DEV: + break; + } + buffer = kmalloc(bytes, GFP_USER); + if (!buffer) + return -ENOMEM; + if (hdr.dxfer_direction == SG_DXFER_TO_DEV || + hdr.dxfer_direction == SG_DXFER_TO_FROM_DEV) + copy_from_user(buffer, hdr.dxferp, hdr.dxfer_len); + } + + rq = blk_get_request(q, WRITE, __GFP_WAIT); + rq->timeout = 60*HZ; + rq->data = buffer; + rq->data_len = hdr.dxfer_len; + rq->flags = REQ_BLOCK_PC; + memset(rq->cmd, 0, sizeof(rq->cmd)); + copy_from_user(rq->cmd, hdr.cmdp, hdr.cmd_len); + err = blk_do_rq(q, rq); + + blk_put_request(rq); + + copy_to_user(uptr, &hdr, sizeof(*uptr)); + if (buffer) { + if (hdr.dxfer_direction == SG_DXFER_FROM_DEV || + hdr.dxfer_direction == SG_DXFER_TO_FROM_DEV) + copy_to_user(hdr.dxferp, buffer, hdr.dxfer_len); + kfree(buffer); + } + return err; +} + +int scsi_cmd_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg) +{ + request_queue_t *q; + struct request *rq; + int close = 0, err; + + q = bdev_get_queue(bdev); + if (!q) + return -ENXIO; + + switch (cmd) { + case SG_GET_VERSION_NUM: + return sg_get_version((int *) arg); + case SCSI_IOCTL_GET_IDLUN: + return scsi_get_idlun(q, (int *) arg); + case SCSI_IOCTL_GET_BUS_NUMBER: + return scsi_get_bus(q, (int *) arg); + case SG_SET_TIMEOUT: + return sg_set_timeout(q, (int *) arg); + case SG_GET_TIMEOUT: + return sg_get_timeout(q); + case SG_GET_RESERVED_SIZE: + return sg_get_reserved_size(q, (int *) arg); + case SG_SET_RESERVED_SIZE: + return sg_set_reserved_size(q, (int *) arg); + case SG_EMULATED_HOST: + return sg_emulated_host(q, (int *) arg); + case SG_IO: + return sg_io(q, (struct sg_io_hdr *) arg); + case CDROMCLOSETRAY: + close = 1; + case CDROMEJECT: + rq = blk_get_request(q, WRITE, __GFP_WAIT); + rq->flags = REQ_BLOCK_PC; + rq->data = NULL; + rq->data_len = 0; + rq->timeout = 60*HZ; + memset(rq->cmd, 0, sizeof(rq->cmd)); + rq->cmd[0] = GPCMD_START_STOP_UNIT; + rq->cmd[4] = 0x02 + (close != 0); + err = blk_do_rq(q, rq); + blk_put_request(rq); + break; + default: + err = -ENOTTY; + } + + blk_put_queue(q); + return err; +} + +EXPORT_SYMBOL(scsi_cmd_ioctl); diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index e2380bcb9fe8..00830680bb42 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -2639,7 +2639,7 @@ static int ide_ioctl (struct inode *inode, struct file *file, case CDROMEJECT: case CDROMCLOSETRAY: - return block_ioctl(inode->i_bdev, cmd, arg); + return scsi_cmd_ioctl(inode->i_bdev, cmd, arg); case HDIO_GET_BUSSTATE: if (!capable(CAP_SYS_ADMIN)) diff --git a/fs/block_dev.c b/fs/block_dev.c index 3b95ff2d40a4..7a3f43f1b186 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -825,44 +825,9 @@ static int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd, unsigned long arg) { struct block_device *bdev = inode->i_bdev; - int ret = -EINVAL; - switch (cmd) { - /* - * deprecated, use the /proc/iosched interface instead - */ - case BLKELVGET: - case BLKELVSET: - ret = -ENOTTY; - break; - case BLKRAGET: - case BLKROGET: - case BLKBSZGET: - case BLKSSZGET: - case BLKFRAGET: - case BLKSECTGET: - case BLKRASET: - case BLKFRASET: - case BLKBSZSET: - case BLKPG: - ret = blk_ioctl(bdev, cmd, arg); - break; - case BLKRRPART: - ret = blkdev_reread_part(bdev); - break; - default: - if (bdev->bd_op->ioctl) - ret =bdev->bd_op->ioctl(inode, file, cmd, arg); - if (ret == -EINVAL) { - switch (cmd) { - case BLKGETSIZE: - case BLKGETSIZE64: - case BLKFLSBUF: - case BLKROSET: - ret = blk_ioctl(bdev,cmd,arg); - break; - } - } - } + int ret = blk_ioctl(bdev, cmd, arg); + if (ret == -ENOTTY && bdev->bd_op->ioctl) + ret = bdev->bd_op->ioctl(inode, file, cmd, arg); return ret; } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 4929d743683d..607641c6cfb1 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -60,6 +60,12 @@ struct request { int tag; void *special; char *buffer; + + /* For packet commands */ + unsigned int data_len; + void *data, *sense; + + unsigned int timeout; struct completion *waiting; struct bio *bio, *biotail; request_queue_t *q; @@ -85,6 +91,8 @@ enum rq_flag_bits { __REQ_BLOCK_PC, /* queued down pc from block layer */ __REQ_SENSE, /* sense retrival */ + __REQ_FAILED, /* set if the request failed */ + __REQ_QUIET, /* don't worry about errors */ __REQ_SPECIAL, /* driver suplied command */ __REQ_DRIVE_CMD, __REQ_DRIVE_TASK, @@ -103,6 +111,8 @@ enum rq_flag_bits { #define REQ_PC (1 << __REQ_PC) #define REQ_BLOCK_PC (1 << __REQ_BLOCK_PC) #define REQ_SENSE (1 << __REQ_SENSE) +#define REQ_FAILED (1 << __REQ_FAILED) +#define REQ_QUIET (1 << __REQ_QUIET) #define REQ_SPECIAL (1 << __REQ_SPECIAL) #define REQ_DRIVE_CMD (1 << __REQ_DRIVE_CMD) #define REQ_DRIVE_TASK (1 << __REQ_DRIVE_TASK) @@ -301,7 +311,7 @@ extern int blk_remove_plug(request_queue_t *); extern void blk_recount_segments(request_queue_t *, struct bio *); extern inline int blk_phys_contig_segment(request_queue_t *q, struct bio *, struct bio *); extern inline int blk_hw_contig_segment(request_queue_t *q, struct bio *, struct bio *); -extern int block_ioctl(struct block_device *, unsigned int, unsigned long); +extern int scsi_cmd_ioctl(struct block_device *, unsigned int, unsigned long); extern void blk_start_queue(request_queue_t *q); extern void blk_stop_queue(request_queue_t *q); extern void __blk_stop_queue(request_queue_t *q); -- cgit v1.2.3 From b8ed178862df2381b14b12c9b3c4f7f39053c5e5 Mon Sep 17 00:00:00 2001 From: Alexander Viro Date: Tue, 15 Oct 2002 04:23:25 -0700 Subject: [PATCH] early allocation of ->part allocation of ->part[] moved to alloc_disk(); alloc_disk() got an argument (number of minors expected). Freeing is in put_disk(). --- arch/m68k/atari/stram.c | 3 +-- arch/um/drivers/ubd_kern.c | 6 ++--- drivers/acorn/block/fd1772.c | 2 +- drivers/acorn/block/mfmhd.c | 3 +-- drivers/block/DAC960.c | 3 +-- drivers/block/acsi.c | 2 +- drivers/block/amiflop.c | 3 +-- drivers/block/ataflop.c | 2 +- drivers/block/cciss.c | 9 ++++--- drivers/block/cpqarray.c | 7 +++--- drivers/block/floppy.c | 2 +- drivers/block/genhd.c | 52 ++++++++++++++++++++--------------------- drivers/block/loop.c | 2 +- drivers/block/nbd.c | 3 +-- drivers/block/paride/pcd.c | 3 +-- drivers/block/paride/pd.c | 3 +-- drivers/block/paride/pf.c | 3 +-- drivers/block/ps2esdi.c | 3 +-- drivers/block/rd.c | 6 ++--- drivers/block/swim3.c | 2 +- drivers/block/swim_iop.c | 2 +- drivers/block/umem.c | 3 +-- drivers/block/xd.c | 3 +-- drivers/block/z2ram.c | 3 +-- drivers/cdrom/aztcd.c | 3 +-- drivers/cdrom/cdu31a.c | 3 +-- drivers/cdrom/cm206.c | 3 +-- drivers/cdrom/gscd.c | 3 +-- drivers/cdrom/mcd.c | 3 +-- drivers/cdrom/mcdx.c | 3 +-- drivers/cdrom/optcd.c | 3 +-- drivers/cdrom/sbpcd.c | 3 +-- drivers/cdrom/sjcd.c | 3 +-- drivers/cdrom/sonycd535.c | 3 +-- drivers/ide/ide-probe.c | 3 +-- drivers/ide/legacy/hd.c | 3 +-- drivers/md/md.c | 3 +-- drivers/message/i2o/i2o_block.c | 3 +-- drivers/mtd/ftl.c | 3 +-- drivers/mtd/mtdblock.c | 3 +-- drivers/mtd/mtdblock_ro.c | 2 +- drivers/mtd/nftlcore.c | 3 +-- drivers/s390/block/dasd_genhd.c | 3 +-- drivers/s390/block/xpram.c | 3 +-- drivers/sbus/char/jsflash.c | 3 +-- drivers/scsi/sd.c | 3 +-- drivers/scsi/sr.c | 3 +-- fs/partitions/check.c | 5 +--- include/linux/genhd.h | 3 ++- 49 files changed, 81 insertions(+), 125 deletions(-) (limited to 'include') diff --git a/arch/m68k/atari/stram.c b/arch/m68k/atari/stram.c index f40e6f70df3b..5d6eac53280a 100644 --- a/arch/m68k/atari/stram.c +++ b/arch/m68k/atari/stram.c @@ -1057,7 +1057,7 @@ int __init stram_device_init(void) if (!max_swap_size) /* swapping not enabled */ return -ENXIO; - stram_disk = alloc_disk(); + stram_disk = alloc_disk(1); if (!stram_disk) return -ENOMEM; @@ -1070,7 +1070,6 @@ int __init stram_device_init(void) blk_init_queue(BLK_DEFAULT_QUEUE(STRAM_MAJOR), do_stram_request); stram_disk->major = STRAM_MAJOR; stram_disk->first_minor = STRAM_MINOR; - stram_disk->minor_shift = 0; stram_disk->fops = &stram_fops; sprintf(stram_disk->disk_name, "stram"); set_capacity(stram_disk, (swap_end - swap_start)/512); diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c index 36995c3f84f6..9229a26c1d16 100644 --- a/arch/um/drivers/ubd_kern.c +++ b/arch/um/drivers/ubd_kern.c @@ -404,12 +404,11 @@ static int ubd_add(int n) if (!dev->file) return -1; - disk = alloc_disk(); + disk = alloc_disk(1 << UBD_SHIFT); if (!disk) return -1; disk->major = MAJOR_NR; disk->first_minor = n << UBD_SHIFT; - disk->minor_shift = UBD_SHIFT; disk->fops = &ubd_blops; if (fakehd_set) sprintf(disk->disk_name, "hd%c", n + 'a'); @@ -417,14 +416,13 @@ static int ubd_add(int n) sprintf(disk->disk_name, "ubd%d", n); if (fake_major) { - fake_disk = alloc_disk(); + fake_disk = alloc_disk(1 << UBD_SHIFT); if (!fake_disk) { put_disk(disk); return -1; } fake_disk->major = fake_major; fake_disk->first_minor = n << UBD_SHIFT; - fake_disk->minor_shift = UBD_SHIFT; fake_disk->fops = &ubd_blops; sprintf(fake_disk->disk_name, "ubd%d", n); fake_gendisk[n] = fake_disk; diff --git a/drivers/acorn/block/fd1772.c b/drivers/acorn/block/fd1772.c index 85b5b9cd8859..1285b8388421 100644 --- a/drivers/acorn/block/fd1772.c +++ b/drivers/acorn/block/fd1772.c @@ -1547,7 +1547,7 @@ int fd1772_init(void) return 0; for (i = 0; i < FD_MAX_UNITS; i++) { - disks[i] = alloc_disk(); + disks[i] = alloc_disk(1); if (!disks[i]) goto out; } diff --git a/drivers/acorn/block/mfmhd.c b/drivers/acorn/block/mfmhd.c index 32bef8806190..b29cb6ad5505 100644 --- a/drivers/acorn/block/mfmhd.c +++ b/drivers/acorn/block/mfmhd.c @@ -1336,12 +1336,11 @@ static int __init mfm_init (void) goto out3; for (i = 0; i < mfm_drives; i++) { - struct gendisk *disk = alloc_disk(); + struct gendisk *disk = alloc_disk(64); if (!disk) goto Enomem; disk->major = MAJOR_NR; disk->first_minor = i << 6; - disk->minor_shift = 6; disk->fops = &mfm_fops; sprintf(disk->disk_name, "mfm%c", 'a'+i); mfm_gendisk[i] = disk; diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index 24a1ee66d93b..1c1a72e440e6 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -1962,7 +1962,6 @@ static boolean DAC960_RegisterBlockDevice(DAC960_Controller_T *Controller) sprintf(disk->disk_name, "rd/c%dd%d", Controller->ControllerNumber, n); disk->major = MajorNumber; disk->first_minor = n << DAC960_MaxPartitionsBits; - disk->minor_shift = DAC960_MaxPartitionsBits; disk->fops = &DAC960_BlockDeviceOperations; } /* @@ -2200,7 +2199,7 @@ static void DAC960_DetectControllers(DAC960_HardwareType_T HardwareType) } memset(Controller, 0, sizeof(DAC960_Controller_T)); for (i = 0; i < DAC960_MaxLogicalDrives; i++) { - Controller->disks[i] = alloc_disk(); + Controller->disks[i] = alloc_disk(1<disks[i]) goto Enomem; } diff --git a/drivers/block/acsi.c b/drivers/block/acsi.c index 5d36adb832e2..006ff8b23e32 100644 --- a/drivers/block/acsi.c +++ b/drivers/block/acsi.c @@ -1729,7 +1729,7 @@ int acsi_init( void ) #endif err = -ENOMEM; for( i = 0; i < NDevices; ++i ) { - acsi_gendisk[i] = alloc_disk(); + acsi_gendisk[i] = alloc_disk(16); if (!acsi_gendisk[i]) goto out4; } diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c index 22790c4145fe..df7d1ac10fe9 100644 --- a/drivers/block/amiflop.c +++ b/drivers/block/amiflop.c @@ -1735,7 +1735,7 @@ static int __init fd_probe_drives(void) fd_probe(drive); if (unit[drive].type->code == FD_NODRIVE) continue; - disk = alloc_disk(); + disk = alloc_disk(1); if (!disk) { unit[drive].type->code = FD_NODRIVE; continue; @@ -1751,7 +1751,6 @@ static int __init fd_probe_drives(void) printk("fd%d ",drive); disk->major = MAJOR_NR; disk->first_minor = drive; - disk->minor_shift = 0; disk->fops = &floppy_fops; sprintf(disk->disk_name, "fd%d", drive); set_capacity(disk, 880*2); diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c index 12f3ae02b317..eac85e42887f 100644 --- a/drivers/block/ataflop.c +++ b/drivers/block/ataflop.c @@ -1949,7 +1949,7 @@ int __init atari_floppy_init (void) } for (i = 0; i < FD_MAX_UNITS; i++) { - unit[i].disk = alloc_disk(); + unit[i].disk = alloc_disk(1); if (!unit[i].disk) goto Enomem; } diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index ebd7a216810f..caa30e1c6e84 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -740,7 +740,7 @@ static int revalidate_allvol(kdev_t dev) for(i=0; i< NWD; i++) { struct gendisk *disk = hba[ctlr]->gendisk[i]; - if (disk->part) + if (disk->flags & GENHD_FL_UP) del_gendisk(disk); } @@ -792,7 +792,7 @@ static int deregister_disk(int ctlr, int logvol) spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); /* invalidate the devices and deregister the disk */ - if (disk->part) + if (disk->flags & GENHD_FL_UP) del_gendisk(disk); /* check to see if it was the last disk */ if (logvol == h->highest_lun) { @@ -2274,7 +2274,7 @@ static int alloc_cciss_hba(void) struct gendisk *disk[NWD]; int i, n; for (n = 0; n < NWD; n++) { - disk[n] = alloc_disk(); + disk[n] = alloc_disk(1 << NWD_SHIFT); if (!disk[n]) goto out; } @@ -2447,7 +2447,6 @@ static int __init cciss_init_one(struct pci_dev *pdev, sprintf(disk->disk_name, "cciss/c%dd%d", i, j); disk->major = MAJOR_NR + i; disk->first_minor = j << NWD_SHIFT; - disk->minor_shift = NWD_SHIFT; if( !(drv->nr_blocks)) continue; (BLK_DEFAULT_QUEUE(MAJOR_NR + i))->hardsect_size = drv->block_size; @@ -2500,7 +2499,7 @@ static void __devexit cciss_remove_one (struct pci_dev *pdev) /* remove it from the disk list */ for (j = 0; j < NWD; j++) { struct gendisk *disk = hba[i]->gendisk[j]; - if (disk->part) + if (disk->flags & GENHD_FL_UP) del_gendisk(disk); } diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index 7bfa29a5bc89..c3b1c4b17ea7 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -304,7 +304,7 @@ static void __exit cpqarray_exit(void) kfree(hba[i]->cmd_pool_bits); for (j = 0; j < NWD; j++) { - if (ida_gendisk[i][j]->part) + if (ida_gendisk[i][j]->flags & GENHD_FL_UP) del_gendisk(ida_gendisk[i][j]); put_disk(ida_gendisk[i][j]); } @@ -358,7 +358,7 @@ static int __init cpqarray_init(void) } num_cntlrs_reg++; for (j=0; jdisk_name, "ida/c%dd%d", i, j); disk->major = MAJOR_NR + i; disk->first_minor = j<minor_shift = NWD_SHIFT; disk->flags = GENHD_FL_DEVFS; disk->fops = &ida_fops; if (!drv->nr_blks) @@ -1428,7 +1427,7 @@ static int revalidate_allvol(kdev_t dev) */ for (i = 0; i < NWD; i++) { struct gendisk *disk = ida_gendisk[ctlr][i]; - if (disk->part) + if (disk->flags & GENDH_FL_UP) del_gendisk(disk); } memset(hba[ctlr]->drv, 0, sizeof(drv_info_t)*NWD); diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 924e1e011f76..8783ee17314d 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -4240,7 +4240,7 @@ int __init floppy_init(void) raw_cmd = NULL; for (i=0; iminor_shift) { - size_t size = sizeof(struct hd_struct)*((1<minor_shift)-1); - p = kmalloc(size, GFP_KERNEL); - if (!p) { - printk(KERN_ERR "out of memory; no partitions for %s\n", - gp->disk_name); - gp->minor_shift = 0; - } else - memset(p, 0, size); - } - gp->part = p; - write_lock(&gendisk_lock); - list_add(&gp->list, &gendisks[gp->major].list); - if (gp->minor_shift) - list_add_tail(&gp->full_list, &gendisk_list); + list_add(&disk->list, &gendisks[disk->major].list); + if (disk->minor_shift) + list_add_tail(&disk->full_list, &gendisk_list); else - INIT_LIST_HEAD(&gp->full_list); + INIT_LIST_HEAD(&disk->full_list); write_unlock(&gendisk_lock); -} - -void add_disk(struct gendisk *disk) -{ - add_gendisk(disk); + disk->flags |= GENHD_FL_UP; register_disk(disk); } @@ -225,17 +207,33 @@ __initcall(device_init); EXPORT_SYMBOL(disk_devclass); -struct gendisk *alloc_disk(void) +struct gendisk *alloc_disk(int minors) { struct gendisk *disk = kmalloc(sizeof(struct gendisk), GFP_KERNEL); - if (disk) + if (disk) { memset(disk, 0, sizeof(struct gendisk)); + if (minors > 1) { + int size = (minors - 1) * sizeof(struct hd_struct); + disk->part = kmalloc(size, GFP_KERNEL); + if (!disk->part) { + kfree(disk); + return NULL; + } + memset(disk->part, 0, size); + } + disk->minors = minors; + while (minors >>= 1) + disk->minor_shift++; + } return disk; } void put_disk(struct gendisk *disk) { - kfree(disk); + if (disk) { + kfree(disk->part); + kfree(disk); + } } EXPORT_SYMBOL(alloc_disk); EXPORT_SYMBOL(put_disk); diff --git a/drivers/block/loop.c b/drivers/block/loop.c index e39755017faf..14fa8720f8db 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1075,7 +1075,7 @@ int __init loop_init(void) goto out_mem; for (i = 0; i < max_loop; i++) { - disks[i] = alloc_disk(); + disks[i] = alloc_disk(1); if (!disks[i]) goto out_mem2; } diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index be27027d32b8..27726bd0246a 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -507,7 +507,7 @@ static int __init nbd_init(void) } for (i = 0; i < MAX_NBD; i++) { - struct gendisk *disk = alloc_disk(); + struct gendisk *disk = alloc_disk(1); if (!disk) goto out; nbd_dev[i].disk = disk; @@ -537,7 +537,6 @@ static int __init nbd_init(void) nbd_bytesizes[i] = 0x7ffffc00; /* 2GB */ disk->major = MAJOR_NR; disk->first_minor = i; - disk->minor_shift = 0; disk->fops = &nbd_fops; sprintf(disk->disk_name, "nbd%d", i); set_capacity(disk, 0x3ffffe); diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c index 0e4bac2bd1ef..95bedb2a580c 100644 --- a/drivers/block/paride/pcd.c +++ b/drivers/block/paride/pcd.c @@ -281,7 +281,7 @@ static void pcd_init_units(void) pcd_drive_count = 0; for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) { - struct gendisk *disk = alloc_disk(); + struct gendisk *disk = alloc_disk(1); if (!disk) continue; cd->disk = disk; @@ -303,7 +303,6 @@ static void pcd_init_units(void) cd->info.mask = 0; disk->major = major; disk->first_minor = unit; - disk->minor_shift = 0; strcpy(disk->disk_name, cd->name); /* umm... */ disk->fops = &pcd_bdops; } diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c index 7fdf4a3e4b2a..3a3ad6390118 100644 --- a/drivers/block/paride/pd.c +++ b/drivers/block/paride/pd.c @@ -703,14 +703,13 @@ static int pd_detect(void) } for (unit = 0, disk = pd; unit < PD_UNITS; unit++, disk++) { if (disk->present) { - struct gendisk *p = alloc_disk(); + struct gendisk *p = alloc_disk(1 << PD_BITS); if (!p) { disk->present = 0; k--; continue; } strcpy(p->disk_name, disk->name); - p->minor_shift = PD_BITS; p->fops = &pd_fops; p->major = major; p->first_minor = unit << PD_BITS; diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c index becf37efd5ec..69a2c8e23eae 100644 --- a/drivers/block/paride/pf.c +++ b/drivers/block/paride/pf.c @@ -308,7 +308,7 @@ void pf_init_units(void) pf_drive_count = 0; for (unit = 0, pf = units; unit < PF_UNITS; unit++, pf++) { - struct gendisk *disk = alloc_disk(); + struct gendisk *disk = alloc_disk(1); if (!disk) continue; pf->disk = disk; @@ -320,7 +320,6 @@ void pf_init_units(void) disk->major = MAJOR_NR; disk->first_minor = unit; strcpy(disk->disk_name, pf->name); - disk->minor_shift = 0; disk->fops = &pf_fops; if (!(*drives[unit])[D_PRT]) pf_drive_count++; diff --git a/drivers/block/ps2esdi.c b/drivers/block/ps2esdi.c index 770fbfd4613f..ed022ff34e3e 100644 --- a/drivers/block/ps2esdi.c +++ b/drivers/block/ps2esdi.c @@ -421,13 +421,12 @@ static int __init ps2esdi_geninit(void) error = -ENOMEM; for (i = 0; i < ps2esdi_drives; i++) { - struct gendisk *disk = alloc_disk(); + struct gendisk *disk = alloc_disk(64); if (!disk) goto err_out4; disk->major = MAJOR_NR; disk->first_minor = i<<6; sprintf(disk->disk_name, "ed%c", 'a'+i); - disk->minor_shift = 6; disk->fops = &ps2esdi_fops; ps2esdi_gendisk[i] = disk; } diff --git a/drivers/block/rd.c b/drivers/block/rd.c index a0e60c5972a6..391664b9a34f 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -431,17 +431,16 @@ static int __init rd_init (void) } #ifdef CONFIG_BLK_DEV_INITRD - initrd_disk = alloc_disk(); + initrd_disk = alloc_disk(1); if (!initrd_disk) return -ENOMEM; initrd_disk->major = MAJOR_NR; initrd_disk->first_minor = INITRD_MINOR; - initrd_disk->minor_shift = 0; initrd_disk->fops = &rd_bd_op; sprintf(initrd_disk->disk_name, "initrd"); #endif for (i = 0; i < NUM_RAMDISKS; i++) { - rd_disks[i] = alloc_disk(); + rd_disks[i] = alloc_disk(1); if (!rd_disks[i]) goto out; } @@ -460,7 +459,6 @@ static int __init rd_init (void) rd_length[i] = rd_size << 10; disk->major = MAJOR_NR; disk->first_minor = i; - disk->minor_shift = 0; disk->fops = &rd_bd_op; sprintf(disk->disk_name, "rd%d", i); set_capacity(disk, rd_size * 2); diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c index b1cb36f3ca5c..2a5f3afefbfa 100644 --- a/drivers/block/swim3.c +++ b/drivers/block/swim3.c @@ -1037,7 +1037,7 @@ int swim3_init(void) return -ENODEV; for (i = 0; i < floppy_count; i++) { - disks[i] = alloc_disk(); + disks[i] = alloc_disk(1); if (!disks[i]) goto out; } diff --git a/drivers/block/swim_iop.c b/drivers/block/swim_iop.c index 29c2f1696063..3ec747c3f80f 100644 --- a/drivers/block/swim_iop.c +++ b/drivers/block/swim_iop.c @@ -188,7 +188,7 @@ int swimiop_init(void) printk("SWIM-IOP: detected %d installed drives.\n", floppy_count); for (i = 0; i < floppy_count; i++) { - struct gendisk *disk = alloc_disk(); + struct gendisk *disk = alloc_disk(1); if (!disk) continue; disk->major = MAJOR_NR; diff --git a/drivers/block/umem.c b/drivers/block/umem.c index 53dfd2a7c624..3c6a3b8294b8 100644 --- a/drivers/block/umem.c +++ b/drivers/block/umem.c @@ -1190,7 +1190,7 @@ int __init mm_init(void) } for (i = 0; i < num_cards; i++) { - mm_gendisk[i] = alloc_disk(); + mm_gendisk[i] = alloc_disk(1 << MM_SHIFT); if (!mm_gendisk[i]) goto out; } @@ -1203,7 +1203,6 @@ int __init mm_init(void) spin_lock_init(&cards[i].lock); disk->major = major_nr; disk->first_minor = i << MM_SHIFT; - disk->minor_shift = MM_SHIFT; disk->fops = &mm_fops; set_capacity(disk, cards[i].mm_size << 1); add_disk(disk); diff --git a/drivers/block/xd.c b/drivers/block/xd.c index 3e3315e81bde..4467ba777d60 100644 --- a/drivers/block/xd.c +++ b/drivers/block/xd.c @@ -205,12 +205,11 @@ static int __init xd_init(void) goto out3; for (i = 0; i < xd_drives; i++) { - struct gendisk *disk = alloc_disk(); + struct gendisk *disk = alloc_disk(64); if (!disk) goto Enomem; disk->major = MAJOR_NR; disk->first_minor = i<<6; - disk->minor_shift = 6; sprintf(disk->disk_name, "xd%c", i+'a'); disk->fops = &xd_fops; xd_gendisk[i] = disk; diff --git a/drivers/block/z2ram.c b/drivers/block/z2ram.c index 30625811de3e..edb2676680e3 100644 --- a/drivers/block/z2ram.c +++ b/drivers/block/z2ram.c @@ -365,14 +365,13 @@ z2_init( void ) MAJOR_NR ); return -EBUSY; } - z2ram_gendisk = alloc_disk(); + z2ram_gendisk = alloc_disk(1); if (!z2ram_gendisk) { unregister_blkdev( MAJOR_NR, DEVICE_NAME ); return -ENOMEM; } z2ram_gendisk->major = MAJOR_NR; z2ram_gendisk->first_minor = 0; - z2ram_gendisk->minor_shift = 0; z2ram_gendisk->fops = &z2_fops; sprintf(z2ram_gendisk->disk_name, "z2ram"); diff --git a/drivers/cdrom/aztcd.c b/drivers/cdrom/aztcd.c index 53f8fe2bafe2..b8e1880d8714 100644 --- a/drivers/cdrom/aztcd.c +++ b/drivers/cdrom/aztcd.c @@ -1908,7 +1908,7 @@ static int __init aztcd_init(void) } devfs_register(NULL, "aztcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0, S_IFBLK | S_IRUGO | S_IWUGO, &azt_fops, NULL); - azt_disk = alloc_disk(); + azt_disk = alloc_disk(1); if (!azt_disk) goto err_out2; if (register_blkdev(MAJOR_NR, "aztcd", &azt_fops) != 0) { @@ -1921,7 +1921,6 @@ static int __init aztcd_init(void) blk_queue_hardsect_size(BLK_DEFAULT_QUEUE(MAJOR_NR), 2048); azt_disk->major = MAJOR_NR; azt_disk->first_minor = 0; - azt_disk->minor_shift = 0; azt_disk->fops = &azt_fops; sprintf(azt_disk->disk_name, "aztcd"); add_disk(azt_disk); diff --git a/drivers/cdrom/cdu31a.c b/drivers/cdrom/cdu31a.c index 8863cb1254de..f4077094707a 100644 --- a/drivers/cdrom/cdu31a.c +++ b/drivers/cdrom/cdu31a.c @@ -3366,12 +3366,11 @@ int __init cdu31a_init(void) goto errout2; } - disk = alloc_disk(); + disk = alloc_disk(1); if (!disk) goto errout1; disk->major = MAJOR_NR; disk->first_minor = 0; - disk->minor_shift = 0; sprintf(disk->disk_name, "cdu31a"); disk->fops = &scd_bdops; disk->flags = GENHD_FL_CD; diff --git a/drivers/cdrom/cm206.c b/drivers/cdrom/cm206.c index 0da8b3bcdf30..8a83a381bcc1 100644 --- a/drivers/cdrom/cm206.c +++ b/drivers/cdrom/cm206.c @@ -1470,12 +1470,11 @@ int __init cm206_init(void) printk(KERN_INFO "Cannot register for major %d!\n", MAJOR_NR); goto out_blkdev; } - disk = alloc_disk(); + disk = alloc_disk(1); if (!disk) goto out_disk; disk->major = MAJOR_NR; disk->first_minor = 0; - disk->minor_shift = 0; sprintf(disk->disk_name, "cm206"); disk->fops = &cm206_bdops; disk->flags = GENHD_FL_CD; diff --git a/drivers/cdrom/gscd.c b/drivers/cdrom/gscd.c index 9e8a14ce9374..d82b99f5a4b5 100644 --- a/drivers/cdrom/gscd.c +++ b/drivers/cdrom/gscd.c @@ -972,12 +972,11 @@ static int __init gscd_init(void) i++; } - gscd_disk = alloc_disk(); + gscd_disk = alloc_disk(1); if (!gscd_disk) goto err_out1; gscd_disk->major = MAJOR_NR; gscd_disk->first_minor = 0; - gscd_disk->minor_shift = 0; gscd_disk->fops = &gscd_fops; sprintf(gscd_disk->disk_name, "gscd"); diff --git a/drivers/cdrom/mcd.c b/drivers/cdrom/mcd.c index 39eff9436cbf..e6c72eabda52 100644 --- a/drivers/cdrom/mcd.c +++ b/drivers/cdrom/mcd.c @@ -1031,7 +1031,7 @@ static void mcd_release(struct cdrom_device_info *cdi) int __init mcd_init(void) { - struct gendisk *disk = alloc_disk(); + struct gendisk *disk = alloc_disk(1); int count; unsigned char result[3]; char msg[80]; @@ -1124,7 +1124,6 @@ int __init mcd_init(void) disk->major = MAJOR_NR; disk->first_minor = 0; - disk->minor_shift = 0; sprintf(disk->disk_name, "mcd"); disk->fops = &mcd_bdops; disk->flags = GENHD_FL_CD; diff --git a/drivers/cdrom/mcdx.c b/drivers/cdrom/mcdx.c index 7b6aaace0be1..9747c15b926b 100644 --- a/drivers/cdrom/mcdx.c +++ b/drivers/cdrom/mcdx.c @@ -1076,7 +1076,7 @@ int __init mcdx_init_drive(int drive) return 1; } - disk = alloc_disk(); + disk = alloc_disk(1); if (!disk) { xwarn("init() malloc failed\n"); kfree(stuffp); @@ -1221,7 +1221,6 @@ int __init mcdx_init_drive(int drive) stuffp->info.dev = mk_kdev(MAJOR_NR, drive); disk->major = MAJOR_NR; disk->first_minor = drive; - disk->minor_shift = 0; strcpy(disk->disk_name, stuffp->info.name); disk->fops = &mcdx_bdops; disk->flags = GENHD_FL_CD; diff --git a/drivers/cdrom/optcd.c b/drivers/cdrom/optcd.c index baf39fd6f708..6abce539684e 100644 --- a/drivers/cdrom/optcd.c +++ b/drivers/cdrom/optcd.c @@ -2010,14 +2010,13 @@ static int __init optcd_init(void) "optcd: no Optics Storage CDROM Initialization\n"); return -EIO; } - optcd_disk = alloc_disk(); + optcd_disk = alloc_disk(1); if (!optcd_disk) { printk(KERN_ERR "optcd: can't allocate disk\n"); return -ENOMEM; } optcd_disk->major = MAJOR_NR; optcd_disk->first_minor = 0; - optcd_disk->minor_shift = 0; optcd_disk->fops = &opt_fops; sprintf(optcd_disk->disk_name, "optcd"); if (!request_region(optcd_port, 4, "optcd")) { diff --git a/drivers/cdrom/sbpcd.c b/drivers/cdrom/sbpcd.c index 409aea0c4f0f..22a4ca708c6f 100644 --- a/drivers/cdrom/sbpcd.c +++ b/drivers/cdrom/sbpcd.c @@ -5831,10 +5831,9 @@ int __init sbpcd_init(void) sbpcd_infop->dev = mk_kdev(MAJOR_NR, j); sbpcd_infop->handle = p; p->sbpcd_infop = sbpcd_infop; - disk = alloc_disk(); + disk = alloc_disk(1); disk->major = MAJOR_NR; disk->first_minor = j; - disk->minor_shift = 0; disk->fops = &sbpcd_bdops; strcpy(disk->disk_name, sbpcd_infop->name); disk->flags = GENHD_FL_CD; diff --git a/drivers/cdrom/sjcd.c b/drivers/cdrom/sjcd.c index c04647548625..9dcdda8741b0 100644 --- a/drivers/cdrom/sjcd.c +++ b/drivers/cdrom/sjcd.c @@ -1689,14 +1689,13 @@ static int __init sjcd_init(void) blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_sjcd_request, &sjcd_lock); blk_queue_hardsect_size(BLK_DEFAULT_QUEUE(MAJOR_NR), 2048); - sjcd_disk = alloc_disk(); + sjcd_disk = alloc_disk(1); if (!sjcd_disk) { printk(KERN_ERR "SJCD: can't allocate disk"); goto out1; } sjcd_disk->major = MAJOR_NR, sjcd_disk->first_minor = 0, - sjcd_disk->minor_shift = 0, sjcd_disk->fops = &sjcd_fops, sprintf(sjcd_disk->disk_name, "sjcd"); diff --git a/drivers/cdrom/sonycd535.c b/drivers/cdrom/sonycd535.c index d73013c02bad..68e8103a7223 100644 --- a/drivers/cdrom/sonycd535.c +++ b/drivers/cdrom/sonycd535.c @@ -1605,12 +1605,11 @@ static int __init sony535_init(void) } initialized = 1; - cdu_disk = alloc_disk(); + cdu_disk = alloc_disk(1); if (!cdu_disk) goto out6; cdu_disk->major = MAJOR_NR; cdu_disk->first_minor = 0; - cdu_disk->minor_shift = 0; cdu_disk->fops = &cdu_fops; sprintf(cdu_disk->disk_name, "cdu"); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 6277ce3cb1e0..478bffc6aed8 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -986,7 +986,7 @@ static void init_gendisk (ide_hwif_t *hwif) units = MAX_DRIVES; for (unit = 0; unit < MAX_DRIVES; unit++) { - disks[unit] = alloc_disk(); + disks[unit] = alloc_disk(1 << PARTN_BITS); if (!disks[unit]) goto err_kmalloc_gd; } @@ -996,7 +996,6 @@ static void init_gendisk (ide_hwif_t *hwif) disk->major = hwif->major; disk->first_minor = unit << PARTN_BITS; sprintf(disk->disk_name,"hd%c",'a'+hwif->index*MAX_DRIVES+unit); - disk->minor_shift = PARTN_BITS; disk->fops = ide_fops; hwif->drives[unit].disk = disk; } diff --git a/drivers/ide/legacy/hd.c b/drivers/ide/legacy/hd.c index b0f5f104876d..7dc166b8e646 100644 --- a/drivers/ide/legacy/hd.c +++ b/drivers/ide/legacy/hd.c @@ -802,12 +802,11 @@ static int __init hd_init(void) goto out; for (drive=0 ; drive < NR_HD ; drive++) { - struct gendisk *disk = alloc_disk(); + struct gendisk *disk = alloc_disk(64); if (!disk) goto Enomem; disk->major = MAJOR_NR; disk->first_minor = drive << 6; - disk->minor_shift = 6; disk->fops = &hd_fops; sprintf(disk->disk_name, "hd%c", 'a'+drive); hd_gendisk[drive] = disk; diff --git a/drivers/md/md.c b/drivers/md/md.c index a40c6af55da5..205bb0fdeee0 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1394,12 +1394,11 @@ static int do_md_run(mddev_t * mddev) #endif } - disk = alloc_disk(); + disk = alloc_disk(1); if (!disk) return -ENOMEM; disk->major = MD_MAJOR; disk->first_minor = mdidx(mddev); - disk->minor_shift = 0; sprintf(disk->disk_name, "md%d", mdidx(mddev)); disk->fops = &md_fops; diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c index 0980a0b775c6..b6f8af6193f1 100644 --- a/drivers/message/i2o/i2o_block.c +++ b/drivers/message/i2o/i2o_block.c @@ -1647,7 +1647,7 @@ static int i2o_block_init(void) } for (i = 0; i < MAX_I2OB; i++) { - struct gendisk *disk = alloc_disk(); + struct gendisk *disk = alloc_disk(16); if (!disk) goto oom; i2o_disk[i] = disk; @@ -1679,7 +1679,6 @@ static int i2o_block_init(void) struct gendisk *disk = i2ob_disk + i; disk->major = MAJOR_NR; disk->first_minor = i<<4; - disk->minor_shift = 4; disk->fops = &i2ob_fops; sprintf(disk->disk_name, "i2o/hd%c", 'a' + i); } diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index 341ad2252885..e40e34d3c7d6 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -1223,7 +1223,7 @@ static void ftl_notify_add(struct mtd_info *mtd) } partition = kmalloc(sizeof(partition_t), GFP_KERNEL); - disk = alloc_disk(); + disk = alloc_disk(1 << PART_BITS); if (!partition||!disk) { printk(KERN_WARNING "No memory to scan for FTL on %s\n", @@ -1237,7 +1237,6 @@ static void ftl_notify_add(struct mtd_info *mtd) sprintf(disk->disk_name, "ftl%c", 'a' + device); disk->major = FTL_MAJOR; disk->first_minor = device << 4; - disk->minor_shift = PART_BITS; disk->fops = &ftl_blk_fops; partition->mtd = mtd; partition->disk = disk; diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 1ad148bd3364..6b32d3cfb390 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -295,7 +295,7 @@ static int mtdblock_open(struct inode *inode, struct file *file) spin_unlock(&mtdblks_lock); mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL); - disk = alloc_disk(); + disk = alloc_disk(1); if (!mtdblk || !disk) goto Enomem; memset(mtdblk, 0, sizeof(*mtdblk)); @@ -313,7 +313,6 @@ static int mtdblock_open(struct inode *inode, struct file *file) } disk->major = MAJOR_NR; disk->first_minor = dev; - disk->minor_shift = 0; disk->fops = &mtd_fops; sprintf(disk->disk_name, "mtd%d", dev); mtdblk->disk = disk; diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c index 65b97e3a11df..97e8437a75d5 100644 --- a/drivers/mtd/mtdblock_ro.c +++ b/drivers/mtd/mtdblock_ro.c @@ -224,7 +224,7 @@ int __init init_mtdblock(void) int i; for (i = 0; i < MAX_MTD_DEVICES; i++) { - struct gendisk *disk = alloc_disk(); + struct gendisk *disk = alloc_disk(1); if (!disk) goto out; disk->major = MAJOR_NR; diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index 60d26b10740e..155aa92a9429 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -74,7 +74,7 @@ static void NFTL_setup(struct mtd_info *mtd) } nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL); - gd = alloc_disk(); + gd = alloc_disk(1 << NFTL_PARTN_BITS); if (!nftl || !gd) { kfree(nftl); put_disk(gd); @@ -132,7 +132,6 @@ static void NFTL_setup(struct mtd_info *mtd) sprintf(gd->disk_name, "nftl%c", 'a' + firstfree); gd->major = MAJOR_NR; gd->first_minor = firstfree << NFTL_PARTN_BITS; - gd->minor_shift = NFTL_PARTN_BITS; set_capacity(gd, nftl->nr_sects); nftl->disk = gd; add_disk(gd); diff --git a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c index be6c7dc5aa0a..67597043b718 100644 --- a/drivers/s390/block/dasd_genhd.c +++ b/drivers/s390/block/dasd_genhd.c @@ -190,14 +190,13 @@ dasd_gendisk_alloc(int devindex) } } - gdp = alloc_disk(); + gdp = alloc_disk(1 << DASD_PARTN_BITS); if (!gdp) return ERR_PTR(-ENOMEM); /* Initialize gendisk structure. */ gdp->major = mi->major; gdp->first_minor = index << DASD_PARTN_BITS; - gdp->minor_shift = DASD_PARTN_BITS; gdp->fops = &dasd_device_operations; /* diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c index 80f8b7573a41..4db75e1b7e73 100644 --- a/drivers/s390/block/xpram.c +++ b/drivers/s390/block/xpram.c @@ -441,7 +441,7 @@ static int __init xpram_setup_blkdev(void) int i, rc = -ENOMEM; for (i = 0; i < xpram_devs; i++) { - struct gendisk *disk = alloc_disk(); + struct gendisk *disk = alloc_disk(1); if (!disk) goto out; xpram_disks[i] = disk; @@ -481,7 +481,6 @@ static int __init xpram_setup_blkdev(void) offset += xpram_devices[i].size; disk->major = XPRAM_MAJOR; disk->first_minor = i; - disk->minor_shift = 0; disk->fops = &xpram_devops; sprintf(disk->disk_name, "slram%d", i); set_capacity(disk, xpram_sizes[i] << 1); diff --git a/drivers/sbus/char/jsflash.c b/drivers/sbus/char/jsflash.c index 16386c234938..c0479c824d05 100644 --- a/drivers/sbus/char/jsflash.c +++ b/drivers/sbus/char/jsflash.c @@ -622,7 +622,7 @@ static int jsfd_init(void) err = -ENOMEM; for (i = 0; i < JSF_MAX; i++) { - struct gendisk *disk = alloc_disk(); + struct gendisk *disk = alloc_disk(1); if (!disk) goto out; jsfd_disk[i] = disk; @@ -648,7 +648,6 @@ static int jsfd_init(void) disk->first_minor = i; sprintf(disk->disk_name, "jsfd%d", i); disk->fops = &jsfd_fops; - disk->minor_shift = 0; set_capacity(disk, jdp->dsize >> 9); add_disk(disk); set_device_ro(MKDEV(JSFD_MAJOR, i), 1); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 5863cdcf9bba..1b7abd00b167 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1386,7 +1386,7 @@ static int sd_attach(Scsi_Device * sdp) ((sdp->type != TYPE_DISK) && (sdp->type != TYPE_MOD))) return 0; - gd = alloc_disk(); + gd = alloc_disk(16); if (!gd) return 1; @@ -1423,7 +1423,6 @@ static int sd_attach(Scsi_Device * sdp) gd->de = sdp->de; gd->major = SD_MAJOR(dsk_nr>>4); gd->first_minor = (dsk_nr & 15)<<4; - gd->minor_shift = 4; gd->fops = &sd_fops; if (dsk_nr > 26) sprintf(gd->disk_name, "sd%c%c",'a'+dsk_nr/26-1,'a'+dsk_nr%26); diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index ab50575b899c..05fe1b938eb4 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -757,7 +757,7 @@ void sr_finish() * with loadable modules. */ if (cd->disk) continue; - disk = alloc_disk(); + disk = alloc_disk(1); if (!disk) continue; if (cd->disk) { @@ -766,7 +766,6 @@ void sr_finish() } disk->major = MAJOR_NR; disk->first_minor = i; - disk->minor_shift = 0; strcpy(disk->disk_name, cd->cdi.name); disk->fops = &sr_bdops; disk->flags = GENHD_FL_CD; diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 5976fa3e466f..b3164b9ca071 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -531,10 +531,7 @@ void del_gendisk(struct gendisk *disk) wipe_partitions(disk); unlink_gendisk(disk); devfs_remove_partitions(disk); - if (disk->part) { - kfree(disk->part); - disk->part = NULL; - } + disk->flags &= ~GENHD_FL_UP; } struct dev_name { diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 62781b452fe9..70c58d8b7e86 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -69,6 +69,7 @@ struct hd_struct { #define GENHD_FL_DRIVERFS 2 #define GENHD_FL_DEVFS 4 #define GENHD_FL_CD 8 +#define GENHD_FL_UP 16 struct gendisk { int major; /* major number of driver */ @@ -262,7 +263,7 @@ char *disk_name (struct gendisk *hd, int part, char *buf); extern int rescan_partitions(struct gendisk *disk, struct block_device *bdev); extern void update_partition(struct gendisk *disk, int part); -extern struct gendisk *alloc_disk(void); +extern struct gendisk *alloc_disk(int minors); extern void put_disk(struct gendisk *disk); /* will go away */ -- cgit v1.2.3 From 847c633af8a42e49030ff941fb64fb3ece6c95ef Mon Sep 17 00:00:00 2001 From: Alexander Viro Date: Tue, 15 Oct 2002 04:23:32 -0700 Subject: [PATCH] disk->minor_shift cleanup new field - disk->minors (1 << disk->minor_shift). Almost all uses of ->minor_shift had that form and thus had been replaced. --- drivers/block/acsi.c | 5 ++++- drivers/block/blkpg.c | 6 +++--- drivers/block/genhd.c | 6 +++--- drivers/ide/ide-cd.c | 1 + drivers/ide/ide-disk.c | 1 + drivers/ide/ide-floppy.c | 1 + fs/block_dev.c | 4 ++-- fs/partitions/check.c | 18 +++++++++--------- include/linux/genhd.h | 1 + 9 files changed, 25 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/block/acsi.c b/drivers/block/acsi.c index 006ff8b23e32..7eb385a51182 100644 --- a/drivers/block/acsi.c +++ b/drivers/block/acsi.c @@ -1739,7 +1739,10 @@ int acsi_init( void ) sprintf(disk->disk_name, "ad%c", 'a'+i); disk->major = MAJOR_NR; disk->first_minor = i << 4; - disk->minor_shift = (acsi_info[i].type==HARDDISK)?4:0; + if (acsi_info[i].type != HARDDISK) { + disk->minor_shift = 0; + disk->minors = 1; + } disk->fops = &acsi_fops; set_capacity(disk, acsi_info[i].size); add_disk(disk); diff --git a/drivers/block/blkpg.c b/drivers/block/blkpg.c index 7b55729fa29a..7fff17616401 100644 --- a/drivers/block/blkpg.c +++ b/drivers/block/blkpg.c @@ -97,7 +97,7 @@ int add_partition(struct block_device *bdev, struct blkpg_partition *p) return -EINVAL; if (part) BUG(); - if (p->pno <= 0 || p->pno >= (1 << g->minor_shift)) + if (p->pno <= 0 || p->pno >= g->minors) return -EINVAL; /* partition number in use? */ @@ -105,7 +105,7 @@ int add_partition(struct block_device *bdev, struct blkpg_partition *p) return -EBUSY; /* overlap? */ - for (i = 0; i < (1<minor_shift) - 1; i++) + for (i = 0; i < g->minors - 1; i++) if (!(ppstart+pplength <= g->part[i].start_sect || ppstart >= g->part[i].start_sect + g->part[i].nr_sects)) return -EBUSY; @@ -142,7 +142,7 @@ int del_partition(struct block_device *bdev, struct blkpg_partition *p) return -EINVAL; if (part) BUG(); - if (p->pno <= 0 || p->pno >= (1 << g->minor_shift)) + if (p->pno <= 0 || p->pno >= g->minors) return -EINVAL; /* existing drive and partition? */ diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index b230df7f7b70..8ecb1461f43e 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -61,7 +61,7 @@ void add_disk(struct gendisk *disk) { write_lock(&gendisk_lock); list_add(&disk->list, &gendisks[disk->major].list); - if (disk->minor_shift) + if (disk->minors > 1) list_add_tail(&disk->full_list, &gendisk_list); else INIT_LIST_HEAD(&disk->full_list); @@ -107,7 +107,7 @@ get_gendisk(dev_t dev, int *part) disk = list_entry(p, struct gendisk, list); if (disk->first_minor > minor) continue; - if (disk->first_minor + (1<minor_shift) <= minor) + if (disk->first_minor + disk->minors <= minor) continue; read_unlock(&gendisk_lock); *part = minor - disk->first_minor; @@ -163,7 +163,7 @@ static int show_partition(struct seq_file *part, void *v) sgp->major, sgp->first_minor, (unsigned long long)get_capacity(sgp) >> 1, disk_name(sgp, 0, buf)); - for (n = 0; n < (1<minor_shift) - 1; n++) { + for (n = 0; n < sgp->minors - 1; n++) { if (sgp->part[n].nr_sects == 0) continue; seq_printf(part, "%4d %4d %10llu %s\n", diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 6e5d283aa93a..3471aba90f64 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -3193,6 +3193,7 @@ static int ide_cdrom_attach (ide_drive_t *drive) memset(info, 0, sizeof (struct cdrom_info)); drive->driver_data = info; DRIVER(drive)->busy++; + g->minors = 1; g->minor_shift = 0; g->de = drive->de; g->flags = GENHD_FL_CD; diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 5ff3daf64280..5b0c1ca8e75d 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -1871,6 +1871,7 @@ static int idedisk_attach(ide_drive_t *drive) goto failed; } DRIVER(drive)->busy--; + g->minors = 1 << PARTN_BITS; g->minor_shift = PARTN_BITS; g->de = drive->de; g->flags = drive->removable ? GENHD_FL_REMOVABLE : 0; diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index 60e3aed69166..fca1f92f896d 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -2108,6 +2108,7 @@ static int idefloppy_attach (ide_drive_t *drive) DRIVER(drive)->busy++; idefloppy_setup (drive, floppy); DRIVER(drive)->busy--; + g->minors = 1 << PARTN_BITS; g->minor_shift = PARTN_BITS; g->de = drive->de; g->flags = drive->removable ? GENHD_FL_REMOVABLE : 0; diff --git a/fs/block_dev.c b/fs/block_dev.c index 981b8df8efc8..47db3ea5e63b 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -540,7 +540,7 @@ int check_disk_change(struct block_device *bdev) disk = get_gendisk(bdev->bd_dev, &part); if (bdops->revalidate) bdops->revalidate(dev); - if (disk && disk->minor_shift) + if (disk && disk->minors > 1) bdev->bd_invalidated = 1; return 1; } @@ -799,7 +799,7 @@ static int blkdev_reread_part(struct block_device *bdev) struct gendisk *disk = get_gendisk(bdev->bd_dev, &part); int res = 0; - if (!disk || !disk->minor_shift || bdev != bdev->bd_contains) + if (!disk || disk->minors == 1 || bdev != bdev->bd_contains) return -EINVAL; if (part) BUG(); diff --git a/fs/partitions/check.c b/fs/partitions/check.c index b3164b9ca071..72e71ea060e7 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -130,7 +130,7 @@ static DEVICE_ATTR(type,S_IRUGO,partition_device_type_read,NULL); static void driverfs_create_partitions(struct gendisk *hd) { - int max_p = 1<minor_shift; + int max_p = hd->minors; struct hd_struct *p = hd->part; char name[DEVICE_NAME_SIZE]; char bus_id[BUS_ID_SIZE]; @@ -187,7 +187,7 @@ static void driverfs_create_partitions(struct gendisk *hd) static void driverfs_remove_partitions(struct gendisk *hd) { - int max_p = 1<minor_shift; + int max_p = hd->minors; struct device *dev; struct hd_struct *p; int part; @@ -233,7 +233,7 @@ static void check_partition(struct gendisk *hd, struct block_device *bdev) if (isdigit(state->name[strlen(state->name)-1])) sprintf(state->name, "p"); } - state->limit = 1<minor_shift; + state->limit = hd->minors; for (i = 0; check_part[i]; i++) { int res, j; struct hd_struct *p; @@ -298,7 +298,7 @@ static void devfs_create_partitions(struct gendisk *dev) unsigned int devfs_flags = DEVFS_FL_DEFAULT; char dirname[64], symlink[16]; static devfs_handle_t devfs_handle; - int part, max_p = 1<minor_shift; + int part, max_p = dev->minors; struct hd_struct *p = dev->part; if (dev->flags & GENHD_FL_REMOVABLE) @@ -380,7 +380,7 @@ static void devfs_remove_partitions(struct gendisk *dev) { #ifdef CONFIG_DEVFS_FS int part; - for (part = (1<minor_shift)-1; part--; ) { + for (part = dev->minors-1; part--; ) { devfs_unregister(dev->part[part].de); dev->part[part].de = NULL; } @@ -401,7 +401,7 @@ void register_disk(struct gendisk *disk) devfs_create_cdrom(disk); /* No minors to use for partitions */ - if (!disk->minor_shift) + if (disk->minors == 1) return; /* No such device (e.g., media were just removed) */ @@ -458,7 +458,7 @@ int rescan_partitions(struct gendisk *disk, struct block_device *bdev) if (res) return res; bdev->bd_invalidated = 0; - for (p = 0; p < (1<minor_shift) - 1; p++) { + for (p = 0; p < disk->minors - 1; p++) { disk->part[p].start_sect = 0; disk->part[p].nr_sects = 0; } @@ -466,7 +466,7 @@ int rescan_partitions(struct gendisk *disk, struct block_device *bdev) bdev->bd_op->revalidate(dev); if (get_capacity(disk)) check_partition(disk, bdev); - for (p = 1; p < (1<minor_shift); p++) + for (p = 1; p < disk->minors; p++) update_partition(disk, p); return res; } @@ -495,7 +495,7 @@ fail: static int wipe_partitions(struct gendisk *disk) { - int max_p = 1 << disk->minor_shift; + int max_p = disk->minors; kdev_t devp; int res; int p; diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 70c58d8b7e86..6e1f68900bba 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -74,6 +74,7 @@ struct hd_struct { struct gendisk { int major; /* major number of driver */ int first_minor; + int minors; int minor_shift; /* number of times minor is shifted to get real minor */ char disk_name[16]; /* name of major driver */ -- cgit v1.2.3 From 8b290eb199620bd66049bf972ef0d995576cf3b9 Mon Sep 17 00:00:00 2001 From: Alexander Viro Date: Tue, 15 Oct 2002 04:23:37 -0700 Subject: [PATCH] device_register() splitup new driverfs helpers - device_initialize/device_add and device_del. The latter is device_unregister() sans the final put_device(). The former is splitup of device_register() into initialization and insertion into tree. --- drivers/base/core.c | 92 +++++++++++++++++++++++++++++++------------------- include/linux/device.h | 3 ++ 2 files changed, 60 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/drivers/base/core.c b/drivers/base/core.c index 4fc859d3ab57..83c31723d844 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -149,36 +149,16 @@ void driver_detach(struct device_driver * drv) spin_unlock(&device_lock); } -/** - * device_register - register a device - * @dev: pointer to the device structure - * - * First, make sure that the device has a parent, create - * a directory for it, then add it to the parent's list of - * children. - * - * Maintains a global list of all devices, in depth-first ordering. - * The head for that list is device_root.g_list. - */ -int device_register(struct device *dev) +int device_add(struct device *dev) { int error; if (!dev || !strlen(dev->bus_id)) return -EINVAL; - INIT_LIST_HEAD(&dev->node); - INIT_LIST_HEAD(&dev->children); - INIT_LIST_HEAD(&dev->g_list); - INIT_LIST_HEAD(&dev->driver_list); - INIT_LIST_HEAD(&dev->bus_list); - INIT_LIST_HEAD(&dev->intf_list); - spin_lock_init(&dev->lock); - atomic_set(&dev->refcount,2); - dev->present = 1; spin_lock(&device_lock); + dev->present = 1; if (dev->parent) { - get_device_locked(dev->parent); list_add_tail(&dev->g_list,&dev->parent->g_list); list_add_tail(&dev->node,&dev->parent->children); } else @@ -209,10 +189,48 @@ int device_register(struct device *dev) list_del_init(&dev->g_list); list_del_init(&dev->node); spin_unlock(&device_lock); - if (dev->parent) - put_device(dev->parent); } - put_device(dev); + return error; +} + +void device_initialize(struct device *dev) +{ + INIT_LIST_HEAD(&dev->node); + INIT_LIST_HEAD(&dev->children); + INIT_LIST_HEAD(&dev->g_list); + INIT_LIST_HEAD(&dev->driver_list); + INIT_LIST_HEAD(&dev->bus_list); + INIT_LIST_HEAD(&dev->intf_list); + spin_lock_init(&dev->lock); + atomic_set(&dev->refcount,1); + if (dev->parent) + get_device(dev->parent); +} + +/** + * device_register - register a device + * @dev: pointer to the device structure + * + * First, make sure that the device has a parent, create + * a directory for it, then add it to the parent's list of + * children. + * + * Maintains a global list of all devices, in depth-first ordering. + * The head for that list is device_root.g_list. + */ +int device_register(struct device *dev) +{ + int error; + + if (!dev || !strlen(dev->bus_id)) + return -EINVAL; + + device_initialize(dev); + if (dev->parent) + get_device(dev->parent); + error = device_add(dev); + if (error && dev->parent) + put_device(dev->parent); return error; } @@ -257,16 +275,7 @@ void put_device(struct device * dev) put_device(parent); } -/** - * device_unregister - unlink device - * @dev: device going away - * - * The device has been removed from the system, so we disavow knowledge - * of it. It might not be the final reference to the device, so we mark - * it as !present, so no more references to it can be acquired. - * In the end, we decrement the final reference count for it. - */ -void device_unregister(struct device * dev) +void device_del(struct device * dev) { spin_lock(&device_lock); dev->present = 0; @@ -293,7 +302,20 @@ void device_unregister(struct device * dev) /* remove the driverfs directory */ device_remove_dir(dev); +} +/** + * device_unregister - unlink device + * @dev: device going away + * + * The device has been removed from the system, so we disavow knowledge + * of it. It might not be the final reference to the device, so we mark + * it as !present, so no more references to it can be acquired. + * In the end, we decrement the final reference count for it. + */ +void device_unregister(struct device * dev) +{ + device_del(dev); put_device(dev); } diff --git a/include/linux/device.h b/include/linux/device.h index 3290c5c40276..80a63939f924 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -329,6 +329,9 @@ dev_set_drvdata (struct device *dev, void *data) */ extern int device_register(struct device * dev); extern void device_unregister(struct device * dev); +extern void device_initialize(struct device * dev); +extern int device_add(struct device * dev); +extern void device_del(struct device * dev); /* driverfs interface for exporting device attributes */ -- cgit v1.2.3 From c6973580141ce5a4904436b29c0dc5f3d9982951 Mon Sep 17 00:00:00 2001 From: Alexander Viro Date: Tue, 15 Oct 2002 04:25:13 -0700 Subject: [PATCH] block ioctl cleanup guts of blkpg.c and blkdev_ioctl() sanitized up and moved into a new file - drivers/block/ioctl.c. blkpg.c is gone. --- drivers/block/Makefile | 2 +- drivers/block/blkpg.c | 310 ---------------------------------------------- drivers/block/floppy.c | 10 -- drivers/block/ioctl.c | 231 ++++++++++++++++++++++++++++++++++ drivers/block/rd.c | 2 - drivers/mtd/mtdblock.c | 2 - drivers/mtd/mtdblock_ro.c | 2 - drivers/mtd/nftlcore.c | 1 - fs/block_dev.c | 29 ----- include/linux/blkpg.h | 1 - include/linux/fs.h | 1 + 11 files changed, 233 insertions(+), 358 deletions(-) delete mode 100644 drivers/block/blkpg.c create mode 100644 drivers/block/ioctl.c (limited to 'include') diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 8457b1bfa13a..6c22bb8963d6 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -11,7 +11,7 @@ export-objs := elevator.o ll_rw_blk.o loop.o genhd.o acsi.o \ scsi_ioctl.o deadline-iosched.o -obj-y := elevator.o ll_rw_blk.o blkpg.o genhd.o scsi_ioctl.o deadline-iosched.o +obj-y := elevator.o ll_rw_blk.o ioctl.o genhd.o scsi_ioctl.o deadline-iosched.o obj-$(CONFIG_MAC_FLOPPY) += swim3.o obj-$(CONFIG_BLK_DEV_FD) += floppy.o diff --git a/drivers/block/blkpg.c b/drivers/block/blkpg.c deleted file mode 100644 index 7fff17616401..000000000000 --- a/drivers/block/blkpg.c +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Partition table and disk geometry handling - * - * This obsoletes the partition-handling code in genhd.c: - * Userspace can look at a disk in arbitrary format and tell - * the kernel what partitions there are on the disk, and how - * these should be numbered. - * It also allows one to repartition a disk that is being used. - * - * A single ioctl with lots of subfunctions: - * - * Device number stuff: - * get_whole_disk() (given the device number of a partition, find - * the device number of the encompassing disk) - * get_all_partitions() (given the device number of a disk, return the - * device numbers of all its known partitions) - * - * Partition stuff: - * add_partition() - * delete_partition() - * test_partition_in_use() (also for test_disk_in_use) - * - * Geometry stuff: - * get_geometry() - * set_geometry() - * get_bios_drivedata() - * - * For today, only the partition stuff - aeb, 990515 - */ - -#include -#include /* for BLKROSET, ... */ -#include /* for capable() */ -#include /* for set_device_ro() */ -#include -#include -#include /* for EXPORT_SYMBOL */ -#include -#include - -#include - -/* - * What is the data describing a partition? - * - * 1. a device number (kdev_t) - * 2. a starting sector and number of sectors (hd_struct) - * given in the part[] array of the gendisk structure for the drive. - * - * The number of sectors is replicated in the sizes[] array of - * the gendisk structure for the major, which again is copied to - * the blk_size[][] array. - * (However, hd_struct has the number of 512-byte sectors, - * g->sizes[] and blk_size[][] have the number of 1024-byte blocks.) - * Note that several drives may have the same major. - */ - -/* - * Add a partition. - * - * returns: EINVAL: bad parameters - * ENXIO: cannot find drive - * EBUSY: proposed partition overlaps an existing one - * or has the same number as an existing one - * 0: all OK. - */ -int add_partition(struct block_device *bdev, struct blkpg_partition *p) -{ - struct gendisk *g; - long long ppstart, pplength; - int part, i; - - /* convert bytes to sectors */ - ppstart = (p->start >> 9); - pplength = (p->length >> 9); - - /* check for fit in a hd_struct */ - if (sizeof(sector_t) == sizeof(long) && - sizeof(long long) > sizeof(long)) { - long pstart, plength; - pstart = ppstart; - plength = pplength; - if (pstart != ppstart || plength != pplength - || pstart < 0 || plength < 0) - return -EINVAL; - } - - /* find the drive major */ - g = get_gendisk(bdev->bd_dev, &part); - if (!g) - return -ENXIO; - - /* existing drive? */ - - /* drive and partition number OK? */ - if (bdev != bdev->bd_contains) - return -EINVAL; - if (part) - BUG(); - if (p->pno <= 0 || p->pno >= g->minors) - return -EINVAL; - - /* partition number in use? */ - if (g->part[p->pno - 1].nr_sects != 0) - return -EBUSY; - - /* overlap? */ - for (i = 0; i < g->minors - 1; i++) - if (!(ppstart+pplength <= g->part[i].start_sect || - ppstart >= g->part[i].start_sect + g->part[i].nr_sects)) - return -EBUSY; - - /* all seems OK */ - g->part[p->pno - 1].start_sect = ppstart; - g->part[p->pno - 1].nr_sects = pplength; - update_partition(g, p->pno); - return 0; -} - -/* - * Delete a partition given by partition number - * - * returns: EINVAL: bad parameters - * ENXIO: cannot find partition - * EBUSY: partition is busy - * 0: all OK. - * - * Note that the dev argument refers to the entire disk, not the partition. - */ -int del_partition(struct block_device *bdev, struct blkpg_partition *p) -{ - struct gendisk *g; - struct block_device *bdevp; - int part; - int holder; - - /* find the drive major */ - g = get_gendisk(bdev->bd_dev, &part); - if (!g) - return -ENXIO; - if (bdev != bdev->bd_contains) - return -EINVAL; - if (part) - BUG(); - if (p->pno <= 0 || p->pno >= g->minors) - return -EINVAL; - - /* existing drive and partition? */ - if (g->part[p->pno - 1].nr_sects == 0) - return -ENXIO; - - /* partition in use? Incomplete check for now. */ - bdevp = bdget(MKDEV(g->major, g->first_minor + p->pno)); - if (!bdevp) - return -ENOMEM; - if (bd_claim(bdevp, &holder) < 0) { - bdput(bdevp); - return -EBUSY; - } - - /* all seems OK */ - fsync_bdev(bdevp); - invalidate_bdev(bdevp, 0); - - g->part[p->pno - 1].start_sect = 0; - g->part[p->pno - 1].nr_sects = 0; - update_partition(g, p->pno); - bd_release(bdevp); - bdput(bdevp); - - return 0; -} - -int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg) -{ - struct blkpg_ioctl_arg a; - struct blkpg_partition p; - int len; - - if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg))) - return -EFAULT; - - switch (a.op) { - case BLKPG_ADD_PARTITION: - case BLKPG_DEL_PARTITION: - len = a.datalen; - if (len < sizeof(struct blkpg_partition)) - return -EINVAL; - if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition))) - return -EFAULT; - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - if (a.op == BLKPG_ADD_PARTITION) - return add_partition(bdev, &p); - else - return del_partition(bdev, &p); - default: - return -EINVAL; - } -} - -/* - * Common ioctl's for block devices - */ -int blk_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg) -{ - request_queue_t *q; - u64 ullval = 0; - int intval; - unsigned short usval; - kdev_t dev = to_kdev_t(bdev->bd_dev); - int holder; - struct backing_dev_info *bdi; - - switch (cmd) { - case BLKROSET: - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - if (get_user(intval, (int *)(arg))) - return -EFAULT; - set_device_ro(dev, intval); - return 0; - case BLKROGET: - intval = (bdev_read_only(bdev) != 0); - return put_user(intval, (int *)(arg)); - - case BLKRASET: - case BLKFRASET: - if(!capable(CAP_SYS_ADMIN)) - return -EACCES; - bdi = blk_get_backing_dev_info(bdev); - if (bdi == NULL) - return -ENOTTY; - bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE; - return 0; - - case BLKRAGET: - case BLKFRAGET: - if (!arg) - return -EINVAL; - bdi = blk_get_backing_dev_info(bdev); - if (bdi == NULL) - return -ENOTTY; - return put_user((bdi->ra_pages * PAGE_CACHE_SIZE) / 512, - (long *)arg); - - case BLKSECTGET: - if ((q = bdev_get_queue(bdev)) == NULL) - return -EINVAL; - - usval = q->max_sectors; - blk_put_queue(q); - return put_user(usval, (unsigned short *)arg); - - case BLKFLSBUF: - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - fsync_bdev(bdev); - invalidate_bdev(bdev, 0); - return 0; - - case BLKSSZGET: - /* get block device hardware sector size */ - intval = bdev_hardsect_size(bdev); - return put_user(intval, (int *) arg); - - case BLKGETSIZE: - { - unsigned long ret; - /* size in sectors, works up to 2 TB */ - ullval = bdev->bd_inode->i_size; - ret = ullval >> 9; - if ((u64)ret != (ullval >> 9)) - return -EFBIG; - return put_user(ret, (unsigned long *) arg); - } - - case BLKGETSIZE64: - /* size in bytes */ - ullval = bdev->bd_inode->i_size; - return put_user(ullval, (u64 *) arg); - - case BLKPG: - return blkpg_ioctl(bdev, (struct blkpg_ioctl_arg *) arg); - case BLKBSZGET: - /* get the logical block size (cf. BLKSSZGET) */ - intval = block_size(bdev); - return put_user(intval, (int *) arg); - - case BLKBSZSET: - /* set the logical block size */ - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - if (!arg) - return -EINVAL; - if (get_user(intval, (int *) arg)) - return -EFAULT; - if (intval > PAGE_SIZE || intval < 512 || - (intval & (intval - 1))) - return -EINVAL; - if (bd_claim(bdev, &holder) < 0) - return -EBUSY; - set_blocksize(bdev, intval); - bd_release(bdev); - return 0; - - default: - return -ENOTTY; - } -} diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 8783ee17314d..3fde460ce7ea 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -3488,16 +3488,6 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, loc.start = 0; return _COPYOUT(loc); } - - case BLKGETSIZE: - ECALL(get_floppy_geometry(drive, type, &g)); - return put_user(g->size, (unsigned long *) param); - - case BLKGETSIZE64: - ECALL(get_floppy_geometry(drive, type, &g)); - return put_user((u64)g->size << 9, (u64 *) param); - /* BLKRRPART is not defined as floppies don't have - * partition tables */ } /* convert the old style command into a new style command */ diff --git a/drivers/block/ioctl.c b/drivers/block/ioctl.c new file mode 100644 index 000000000000..fb6a8edb8e21 --- /dev/null +++ b/drivers/block/ioctl.c @@ -0,0 +1,231 @@ +#include /* for capable() */ +#include /* for set_device_ro() */ +#include +#include +#include +#include + +static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg) +{ + struct block_device *bdevp; + int holder; + struct gendisk *disk; + struct blkpg_ioctl_arg a; + struct blkpg_partition p; + long long start, length; + int part; + int i; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg))) + return -EFAULT; + if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition))) + return -EFAULT; + disk = get_gendisk(bdev->bd_dev, &part); + if (!disk) + return -ENXIO; + if (bdev != bdev->bd_contains) + return -EINVAL; + if (part) + BUG(); + part = p.pno; + if (part <= 0 || part >= disk->minors) + return -EINVAL; + + switch (a.op) { + case BLKPG_ADD_PARTITION: + start = p.start >> 9; + length = p.length >> 9; + /* check for fit in a hd_struct */ + if (sizeof(sector_t) == sizeof(long) && + sizeof(long long) > sizeof(long)) { + long pstart = start, plength = length; + if (pstart != start || plength != length + || pstart < 0 || plength < 0) + return -EINVAL; + } + + /* partition number in use? */ + if (disk->part[part - 1].nr_sects != 0) + return -EBUSY; + + /* overlap? */ + for (i = 0; i < disk->minors - 1; i++) { + struct hd_struct *s = &disk->part[i]; + if (!(start+length <= s->start_sect || + start >= s->start_sect + s->nr_sects)) + return -EBUSY; + } + /* all seems OK */ + disk->part[part - 1].start_sect = start; + disk->part[part - 1].nr_sects = length; + update_partition(disk, part); + return 0; + case BLKPG_DEL_PARTITION: + if (disk->part[part - 1].nr_sects == 0) + return -ENXIO; + + /* partition in use? Incomplete check for now. */ + bdevp = bdget(MKDEV(disk->major, disk->first_minor) + part); + if (!bdevp) + return -ENOMEM; + if (bd_claim(bdevp, &holder) < 0) { + bdput(bdevp); + return -EBUSY; + } + + /* all seems OK */ + fsync_bdev(bdevp); + invalidate_bdev(bdevp, 0); + + disk->part[part].start_sect = 0; + disk->part[part].nr_sects = 0; + update_partition(disk, part); + bd_release(bdevp); + bdput(bdevp); + return 0; + default: + return -EINVAL; + } +} + +static int blkdev_reread_part(struct block_device *bdev) +{ + int part; + struct gendisk *disk = get_gendisk(bdev->bd_dev, &part); + int res = 0; + + if (!disk || disk->minors == 1 || bdev != bdev->bd_contains) + return -EINVAL; + if (part) + BUG(); + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (down_trylock(&bdev->bd_sem)) + return -EBUSY; + res = rescan_partitions(disk, bdev); + up(&bdev->bd_sem); + return res; +} + +static int put_ushort(unsigned long arg, unsigned short val) +{ + return put_user(val, (unsigned short *)arg); +} + +static int put_int(unsigned long arg, int val) +{ + return put_user(val, (int *)arg); +} + +static int put_long(unsigned long arg, long val) +{ + return put_user(val, (long *)arg); +} + +static int put_ulong(unsigned long arg, unsigned long val) +{ + return put_user(val, (unsigned long *)arg); +} + +static int put_u64(unsigned long arg, u64 val) +{ + return put_user(val, (u64 *)arg); +} + +int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd, + unsigned long arg) +{ + struct block_device *bdev = inode->i_bdev; + struct backing_dev_info *bdi; + int holder; + int ret, n; + + switch (cmd) { + case BLKELVGET: + case BLKELVSET: + /* deprecated, use the /proc/iosched interface instead */ + return -ENOTTY; + case BLKRAGET: + case BLKFRAGET: + if (!arg) + return -EINVAL; + bdi = blk_get_backing_dev_info(bdev); + if (bdi == NULL) + return -ENOTTY; + return put_long(arg, (bdi->ra_pages * PAGE_CACHE_SIZE) / 512); + case BLKROGET: + return put_int(arg, bdev_read_only(bdev) != 0); + case BLKBSZGET: /* get the logical block size (cf. BLKSSZGET) */ + return put_int(arg, block_size(bdev)); + case BLKSSZGET: /* get block device hardware sector size */ + return put_int(arg, bdev_hardsect_size(bdev)); + case BLKSECTGET: + return put_ushort(arg, bdev->bd_queue->max_sectors); + case BLKRASET: + case BLKFRASET: + if(!capable(CAP_SYS_ADMIN)) + return -EACCES; + bdi = blk_get_backing_dev_info(bdev); + if (bdi == NULL) + return -ENOTTY; + bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE; + return 0; + case BLKBSZSET: + /* set the logical block size */ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (!arg) + return -EINVAL; + if (get_user(n, (int *) arg)) + return -EFAULT; + if (n > PAGE_SIZE || n < 512 || (n & (n - 1))) + return -EINVAL; + if (bd_claim(bdev, &holder) < 0) + return -EBUSY; + set_blocksize(bdev, n); + bd_release(bdev); + return 0; + case BLKPG: + return blkpg_ioctl(bdev, (struct blkpg_ioctl_arg *) arg); + case BLKRRPART: + return blkdev_reread_part(bdev); + case BLKGETSIZE: + if ((bdev->bd_inode->i_size >> 9) > ~0UL) + return -EFBIG; + return put_ulong(arg, bdev->bd_inode->i_size >> 9); + case BLKGETSIZE64: + return put_u64(arg, bdev->bd_inode->i_size); + case BLKFLSBUF: + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (bdev->bd_op->ioctl) { + ret = bdev->bd_op->ioctl(inode, file, cmd, arg); + if (ret != -EINVAL) + return ret; + } + fsync_bdev(bdev); + invalidate_bdev(bdev, 0); + return 0; + case BLKROSET: + if (bdev->bd_op->ioctl) { + ret = bdev->bd_op->ioctl(inode, file, cmd, arg); + if (ret != -EINVAL) + return ret; + } + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (get_user(n, (int *)(arg))) + return -EFAULT; + set_device_ro(to_kdev_t(bdev->bd_dev), n); + return 0; + default: + if (bdev->bd_op->ioctl) { + ret = bdev->bd_op->ioctl(inode, file, cmd, arg); + if (ret != -EINVAL) + return ret; + } + } + return -ENOTTY; +} diff --git a/drivers/block/rd.c b/drivers/block/rd.c index 391664b9a34f..7d72b786080c 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -291,8 +291,6 @@ static int rd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un if (cmd != BLKFLSBUF) return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; /* special: we want to release the ramdisk memory, it's not like with the other blockdevices where this ioctl only flushes away the buffer cache. */ diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 6b32d3cfb390..a39bcab25891 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -517,8 +517,6 @@ static int mtdblock_ioctl(struct inode * inode, struct file * file, switch (cmd) { case BLKFLSBUF: - if(!capable(CAP_SYS_ADMIN)) - return -EACCES; fsync_bdev(inode->i_bdev); invalidate_bdev(inode->i_bdev, 0); down(&mtdblk->cache_sem); diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c index 97e8437a75d5..1878f540f3b6 100644 --- a/drivers/mtd/mtdblock_ro.c +++ b/drivers/mtd/mtdblock_ro.c @@ -201,8 +201,6 @@ static int mtdblock_ioctl(struct inode * inode, struct file * file, if (!mtd || cmd != BLKFLSBUF) return -EINVAL; - if(!capable(CAP_SYS_ADMIN)) - return -EACCES; fsync_bdev(inode->i_bdev); invalidate_bdev(inode->i_bdev, 0); if (mtd->sync) diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index 155aa92a9429..292894af8252 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -770,7 +770,6 @@ static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0; } case BLKFLSBUF: - if (!capable(CAP_SYS_ADMIN)) return -EACCES; fsync_bdev(inode->i_bdev); invalidate_bdev(inode->i_bdev, 0); if (nftl->mtd->sync) diff --git a/fs/block_dev.c b/fs/block_dev.c index 47db3ea5e63b..dff0244e63a6 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -793,25 +793,6 @@ int blkdev_close(struct inode * inode, struct file * filp) return blkdev_put(inode->i_bdev, BDEV_FILE); } -static int blkdev_reread_part(struct block_device *bdev) -{ - int part; - struct gendisk *disk = get_gendisk(bdev->bd_dev, &part); - int res = 0; - - if (!disk || disk->minors == 1 || bdev != bdev->bd_contains) - return -EINVAL; - if (part) - BUG(); - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - if (down_trylock(&bdev->bd_sem)) - return -EBUSY; - res = rescan_partitions(disk, bdev); - up(&bdev->bd_sem); - return res; -} - static ssize_t blkdev_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { @@ -820,16 +801,6 @@ static ssize_t blkdev_file_write(struct file *file, const char *buf, return generic_file_write_nolock(file, &local_iov, 1, ppos); } -static int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd, - unsigned long arg) -{ - struct block_device *bdev = inode->i_bdev; - int ret = blk_ioctl(bdev, cmd, arg); - if (ret == -ENOTTY && bdev->bd_op->ioctl) - ret = bdev->bd_op->ioctl(inode, file, cmd, arg); - return ret; -} - struct address_space_operations def_blk_aops = { .readpage = blkdev_readpage, .writepage = blkdev_writepage, diff --git a/include/linux/blkpg.h b/include/linux/blkpg.h index 3cfedb07f803..571618972e30 100644 --- a/include/linux/blkpg.h +++ b/include/linux/blkpg.h @@ -57,7 +57,6 @@ struct blkpg_partition { #ifdef __KERNEL__ extern char * partition_name(dev_t dev); -extern int blk_ioctl(struct block_device *bdev, unsigned int cmd, unsigned long arg); #endif /* __KERNEL__ */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 93148f1659b0..cac13f931cec 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1087,6 +1087,7 @@ extern struct file_operations def_blk_fops; extern struct address_space_operations def_blk_aops; extern struct file_operations def_fifo_fops; extern int ioctl_by_bdev(struct block_device *, unsigned, unsigned long); +extern int blkdev_ioctl(struct inode *, struct file *, unsigned, unsigned long); extern int blkdev_get(struct block_device *, mode_t, unsigned, int); extern int blkdev_put(struct block_device *, int); extern int bd_claim(struct block_device *, void *); -- cgit v1.2.3 From afae25b7c8d594f6349e81dce2b16ce44aa9f0ed Mon Sep 17 00:00:00 2001 From: Alexander Viro Date: Tue, 15 Oct 2002 04:25:18 -0700 Subject: [PATCH] preparation to use of driverfs refcounts, part 1 - partitions * update_partition() split into add_partition() and delete_partition(). * all updating of ->part[] is switched to these two (including initial filling/final cleaning). * per-partition devices are allocated on-demand and never reused. We allocate struct device in add_partition() and put reference to it into hd_struct. ->release() for that struct device frees it. delete_partition() removes reference from hd_struct and does put_device() on it. Basically, we get rid of problems with reused struct device by never reusing them... At that point devices for partitions are nice and sane. --- drivers/block/ioctl.c | 8 +- fs/partitions/check.c | 289 ++++++++++++++++++++++---------------------------- include/linux/genhd.h | 5 +- 3 files changed, 134 insertions(+), 168 deletions(-) (limited to 'include') diff --git a/drivers/block/ioctl.c b/drivers/block/ioctl.c index fb6a8edb8e21..4af05bc32db2 100644 --- a/drivers/block/ioctl.c +++ b/drivers/block/ioctl.c @@ -58,9 +58,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg) return -EBUSY; } /* all seems OK */ - disk->part[part - 1].start_sect = start; - disk->part[part - 1].nr_sects = length; - update_partition(disk, part); + add_partition(disk, part, start, length); return 0; case BLKPG_DEL_PARTITION: if (disk->part[part - 1].nr_sects == 0) @@ -79,9 +77,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg) fsync_bdev(bdevp); invalidate_bdev(bdevp, 0); - disk->part[part].start_sect = 0; - disk->part[part].nr_sects = 0; - update_partition(disk, part); + delete_partition(disk, part); bd_release(bdevp); bdput(bdevp); return 0; diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 72e71ea060e7..a61a83ded312 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -130,96 +130,49 @@ static DEVICE_ATTR(type,S_IRUGO,partition_device_type_read,NULL); static void driverfs_create_partitions(struct gendisk *hd) { - int max_p = hd->minors; - struct hd_struct *p = hd->part; - char name[DEVICE_NAME_SIZE]; - char bus_id[BUS_ID_SIZE]; - struct device *dev, *parent; - int part; + struct device *parent = hd->driverfs_dev; + struct device *dev = &hd->disk_dev; /* if driverfs not supported by subsystem, skip partitions */ if (!(hd->flags & GENHD_FL_DRIVERFS)) return; - parent = hd->driverfs_dev; - if (parent) { - sprintf(name, "%s", parent->name); - sprintf(bus_id, "%s:", parent->bus_id); + sprintf(dev->name, "%sdisc", parent->name); + sprintf(dev->bus_id, "%sdisc", parent->bus_id); + dev->parent = parent; + dev->bus = parent->bus; } else { - *name = *bus_id = '\0'; + sprintf(dev->name, "disc"); + sprintf(dev->bus_id, "disc"); } - - dev = &hd->disk_dev; dev->driver_data = (void *)(long)__mkdev(hd->major, hd->first_minor); - sprintf(dev->name, "%sdisc", name); - sprintf(dev->bus_id, "%sdisc", bus_id); - for (part=1; part < max_p; part++) { - dev = &p[part-1].hd_driverfs_dev; - sprintf(dev->name, "%spart%d", name, part); - sprintf(dev->bus_id, "%s:p%d", bus_id, part); - if (!p[part-1].nr_sects) - continue; - dev->driver_data = - (void *)(long)__mkdev(hd->major, hd->first_minor+part); - } - - dev = &hd->disk_dev; - dev->parent = parent; - if (parent) - dev->bus = parent->bus; device_register(dev); device_create_file(dev, &dev_attr_type); device_create_file(dev, &dev_attr_kdev); - - for (part=0; part < max_p-1; part++) { - dev = &p[part].hd_driverfs_dev; - dev->parent = parent; - if (parent) - dev->bus = parent->bus; - if (!dev->driver_data) - continue; - device_register(dev); - device_create_file(dev, &dev_attr_type); - device_create_file(dev, &dev_attr_kdev); - } } static void driverfs_remove_partitions(struct gendisk *hd) { - int max_p = hd->minors; - struct device *dev; - struct hd_struct *p; - int part; - - for (part=1, p = hd->part; part < max_p; part++, p++) { - dev = &p->hd_driverfs_dev; - if (dev->driver_data) { - device_remove_file(dev, &dev_attr_type); - device_remove_file(dev, &dev_attr_kdev); - put_device(dev); - dev->driver_data = NULL; - } - } - dev = &hd->disk_dev; - if (dev->driver_data) { - device_remove_file(dev, &dev_attr_type); - device_remove_file(dev, &dev_attr_kdev); - put_device(dev); - dev->driver_data = NULL; - } + struct device *dev = &hd->disk_dev; + if (!(hd->flags & GENHD_FL_DRIVERFS)) + return; + device_remove_file(dev, &dev_attr_type); + device_remove_file(dev, &dev_attr_kdev); + put_device(dev); } -static void check_partition(struct gendisk *hd, struct block_device *bdev) +static struct parsed_partitions * +check_partition(struct gendisk *hd, struct block_device *bdev) { + struct parsed_partitions *state; devfs_handle_t de = NULL; char buf[64]; - struct parsed_partitions *state; - int i; + int i, res; state = kmalloc(sizeof(struct parsed_partitions), GFP_KERNEL); if (!state) - return; + return NULL; if (hd->flags & GENHD_FL_DEVFS) de = hd->de; @@ -234,31 +187,19 @@ static void check_partition(struct gendisk *hd, struct block_device *bdev) sprintf(state->name, "p"); } state->limit = hd->minors; - for (i = 0; check_part[i]; i++) { - int res, j; - struct hd_struct *p; + i = res = 0; + while (!res && check_part[i]) { memset(&state->parts, 0, sizeof(state->parts)); - res = check_part[i](state, bdev); - if (!res) - continue; - if (res < 0) { - if (warn_no_part) - printk(" unable to read partition table\n"); - return; - } - p = hd->part; - for (j = 1; j < state->limit; j++) { - p[j-1].start_sect = state->parts[j].from; - p[j-1].nr_sects = state->parts[j].size; -#if CONFIG_BLK_DEV_MD - if (!state->parts[j].flags) - continue; - md_autodetect_dev(bdev->bd_dev+j); -#endif - } - return; + res = check_part[i++](state, bdev); } - printk(" unknown partition table\n"); + if (res > 0) + return state; + if (!res) + printk(" unknown partition table\n"); + else if (warn_no_part) + printk(" unable to read partition table\n"); + kfree(state); + return NULL; } static void devfs_register_partition(struct gendisk *dev, int part) @@ -329,9 +270,6 @@ static void devfs_create_partitions(struct gendisk *dev) devfs_auto_unregister(dev->disk_de, slave); if (!(dev->flags & GENHD_FL_DEVFS)) devfs_auto_unregister (slave, dir); - for (part = 1; part < max_p; part++, p++) - if (p->nr_sects) - devfs_register_partition(dev, part); #endif } @@ -379,11 +317,6 @@ static void devfs_create_cdrom(struct gendisk *dev) static void devfs_remove_partitions(struct gendisk *dev) { #ifdef CONFIG_DEVFS_FS - int part; - for (part = dev->minors-1; part--; ) { - devfs_unregister(dev->part[part].de); - dev->part[part].de = NULL; - } devfs_unregister(dev->disk_de); dev->disk_de = NULL; if (dev->flags & GENHD_FL_CD) @@ -393,10 +326,69 @@ static void devfs_remove_partitions(struct gendisk *dev) #endif } +void delete_partition(struct gendisk *disk, int part) +{ + struct hd_struct *p = disk->part + part - 1; + struct device *dev; + if (!p->nr_sects) + return; + p->start_sect = 0; + p->nr_sects = 0; + devfs_unregister(p->de); + dev = p->hd_driverfs_dev; + p->hd_driverfs_dev = NULL; + if (dev) { + device_remove_file(dev, &dev_attr_type); + device_remove_file(dev, &dev_attr_kdev); + device_unregister(dev); + } +} + +static void part_release(struct device *dev) +{ + kfree(dev); +} + +void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len) +{ + struct hd_struct *p = disk->part + part - 1; + struct device *parent = disk->disk_dev.parent; + struct device *dev; + + p->start_sect = start; + p->nr_sects = len; + devfs_register_partition(disk, part); + if (!(disk->flags & GENHD_FL_DRIVERFS)) + return; + dev = kmalloc(sizeof(struct device), GFP_KERNEL); + if (!dev) + return; + memset(dev, 0, sizeof(struct device)); + if (parent) { + sprintf(dev->name, "%spart%d", parent->name, part); + sprintf(dev->bus_id, "%s:p%d", parent->bus_id, part); + dev->parent = parent; + dev->bus = parent->bus; + } else { + sprintf(dev->name, "part%d", part); + sprintf(dev->bus_id, "p%d", part); + } + dev->release = part_release; + dev->driver_data = + (void *)(long)__mkdev(disk->major, disk->first_minor+part); + device_register(dev); + device_create_file(dev, &dev_attr_type); + device_create_file(dev, &dev_attr_kdev); + p->hd_driverfs_dev = dev; +} + /* Not exported, helper to add_disk(). */ void register_disk(struct gendisk *disk) { + struct parsed_partitions *state; struct block_device *bdev; + int j; + if (disk->flags & GENHD_FL_CD) devfs_create_cdrom(disk); @@ -411,45 +403,33 @@ void register_disk(struct gendisk *disk) bdev = bdget(MKDEV(disk->major, disk->first_minor)); if (blkdev_get(bdev, FMODE_READ, 0, BDEV_RAW) < 0) return; - check_partition(disk, bdev); + state = check_partition(disk, bdev); driverfs_create_partitions(disk); devfs_create_partitions(disk); - blkdev_put(bdev, BDEV_RAW); -} - -void update_partition(struct gendisk *disk, int part) -{ - struct hd_struct *p = disk->part + part - 1; - struct device *dev = &p->hd_driverfs_dev; - - if (!p->nr_sects) { - if (p->de) { - devfs_unregister(p->de); - p->de = NULL; - } - if (dev->driver_data) { - device_remove_file(dev, &dev_attr_type); - device_remove_file(dev, &dev_attr_kdev); - put_device(dev); - dev->driver_data = NULL; + if (state) { + for (j = 1; j < state->limit; j++) { + sector_t size = state->parts[j].size; + sector_t from = state->parts[j].from; + if (!size) + continue; + add_partition(disk, j, from, size); +#if CONFIG_BLK_DEV_MD + if (!state->parts[j].flags) + continue; + md_autodetect_dev(bdev->bd_dev+j); +#endif } - return; + kfree(state); } - if (!p->de) - devfs_register_partition(disk, part); - if (dev->driver_data || !(disk->flags & GENHD_FL_DRIVERFS)) - return; - dev->driver_data = - (void *)(long)__mkdev(disk->major, disk->first_minor+part); - device_register(dev); - device_create_file(dev, &dev_attr_type); - device_create_file(dev, &dev_attr_kdev); + blkdev_put(bdev, BDEV_RAW); } int rescan_partitions(struct gendisk *disk, struct block_device *bdev) { kdev_t dev = to_kdev_t(bdev->bd_dev); + struct parsed_partitions *state; int p, res; + if (!bdev->bd_invalidated) return 0; if (bdev->bd_part_count) @@ -458,16 +438,25 @@ int rescan_partitions(struct gendisk *disk, struct block_device *bdev) if (res) return res; bdev->bd_invalidated = 0; - for (p = 0; p < disk->minors - 1; p++) { - disk->part[p].start_sect = 0; - disk->part[p].nr_sects = 0; - } + for (p = 1; p < disk->minors; p++) + delete_partition(disk, p); if (bdev->bd_op->revalidate) bdev->bd_op->revalidate(dev); - if (get_capacity(disk)) - check_partition(disk, bdev); - for (p = 1; p < disk->minors; p++) - update_partition(disk, p); + if (!get_capacity(disk) || !(state = check_partition(disk, bdev))) + return res; + for (p = 1; p < state->limit; p++) { + sector_t size = state->parts[p].size; + sector_t from = state->parts[p].from; + if (!size) + continue; + add_partition(disk, p, from, size); +#if CONFIG_BLK_DEV_MD + if (!state->parts[j].flags) + continue; + md_autodetect_dev(bdev->bd_dev+p); +#endif + } + kfree(state); return res; } @@ -493,45 +482,25 @@ fail: return NULL; } -static int wipe_partitions(struct gendisk *disk) +void del_gendisk(struct gendisk *disk) { int max_p = disk->minors; kdev_t devp; - int res; int p; /* invalidate stuff */ for (p = max_p - 1; p > 0; p--) { devp = mk_kdev(disk->major,disk->first_minor + p); -#if 0 /* %%% superfluous? */ - if (disk->part[p-1].nr_sects == 0) - continue; -#endif - res = invalidate_device(devp, 1); - if (res) - return res; - disk->part[p-1].start_sect = 0; - disk->part[p-1].nr_sects = 0; + invalidate_device(devp, 1); + delete_partition(disk, p); } devp = mk_kdev(disk->major,disk->first_minor); -#if 0 /* %%% superfluous? */ - if (disk->part[p].nr_sects == 0) - continue; -#endif - res = invalidate_device(devp, 1); - if (res) - return res; + invalidate_device(devp, 1); disk->capacity = 0; - return 0; -} - -void del_gendisk(struct gendisk *disk) -{ - driverfs_remove_partitions(disk); - wipe_partitions(disk); + disk->flags &= ~GENHD_FL_UP; unlink_gendisk(disk); + driverfs_remove_partitions(disk); devfs_remove_partitions(disk); - disk->flags &= ~GENHD_FL_UP; } struct dev_name { diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 6e1f68900bba..6b859fad6a8a 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -62,7 +62,7 @@ struct hd_struct { sector_t start_sect; sector_t nr_sects; devfs_handle_t de; /* primary (master) devfs entry */ - struct device hd_driverfs_dev; /* support driverfs hiearchy */ + struct device *hd_driverfs_dev; /* support driverfs hiearchy */ }; #define GENHD_FL_REMOVABLE 1 @@ -262,7 +262,8 @@ struct unixware_disklabel { char *disk_name (struct gendisk *hd, int part, char *buf); extern int rescan_partitions(struct gendisk *disk, struct block_device *bdev); -extern void update_partition(struct gendisk *disk, int part); +extern void add_partition(struct gendisk *, int, sector_t, sector_t); +extern void delete_partition(struct gendisk *, int); extern struct gendisk *alloc_disk(int minors); extern void put_disk(struct gendisk *disk); -- cgit v1.2.3 From b288f6add39cf474fc2ec8087d32d3e1d4c1c6d0 Mon Sep 17 00:00:00 2001 From: Alexander Viro Date: Tue, 15 Oct 2002 04:25:24 -0700 Subject: [PATCH] preparation to use of driverfs refcounts, part 2 - disk * disk->disk_dev is initialized in alloc_disk(), device_add()'d in add_disk(), device_del()'d in unregister_disk() and device_put() in put_disk(). * devices of partitions are made its children. * attributes of disk one: dev (dev_t of the thing), range (number of minors) and size (in sectors). * attributes of partition ones: dev (ditto), start (in sectors) and size (in sectors). * disk devices are put on a new bus - "block" * if caller of add_disk() had set disk->driverfs_dev, we set symlinks: "device" from disk to underlying device and "block" from underlying device to disk. * ->release() of disk_dev frees disk and disk->part. At that point we have sane driverfs subtree for each gendisk and refcount of its root (disk->disk_dev) can act as gendisk refcount. --- drivers/block/genhd.c | 23 ++++- drivers/ide/ide-cd.c | 1 + drivers/ide/ide-disk.c | 1 + drivers/ide/ide-floppy.c | 1 + drivers/scsi/sr.c | 34 +------- fs/partitions/check.c | 221 ++++++++++++++++++++++++++++++++--------------- include/linux/cdrom.h | 1 - 7 files changed, 173 insertions(+), 109 deletions(-) (limited to 'include') diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index 8ecb1461f43e..ecb2dcdf214d 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -192,6 +192,10 @@ struct device_class disk_devclass = { .name = "disk", }; +static struct bus_type disk_bus = { + name: "block", +}; + int __init device_init(void) { int i; @@ -200,6 +204,7 @@ int __init device_init(void) INIT_LIST_HEAD(&gendisks[i].list); blk_dev_init(); devclass_register(&disk_devclass); + bus_register(&disk_bus); return 0; } @@ -207,6 +212,13 @@ __initcall(device_init); EXPORT_SYMBOL(disk_devclass); +static void disk_release(struct device *dev) +{ + struct gendisk *disk = dev->driver_data; + kfree(disk->part); + kfree(disk); +} + struct gendisk *alloc_disk(int minors) { struct gendisk *disk = kmalloc(sizeof(struct gendisk), GFP_KERNEL); @@ -224,16 +236,19 @@ struct gendisk *alloc_disk(int minors) disk->minors = minors; while (minors >>= 1) disk->minor_shift++; + disk->disk_dev.bus = &disk_bus; + disk->disk_dev.release = disk_release; + disk->disk_dev.driver_data = disk; + device_initialize(&disk->disk_dev); } return disk; } void put_disk(struct gendisk *disk) { - if (disk) { - kfree(disk->part); - kfree(disk); - } + if (disk) + put_device(&disk->disk_dev); } + EXPORT_SYMBOL(alloc_disk); EXPORT_SYMBOL(put_disk); diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 3471aba90f64..8fffe423ab14 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -3196,6 +3196,7 @@ static int ide_cdrom_attach (ide_drive_t *drive) g->minors = 1; g->minor_shift = 0; g->de = drive->de; + g->driverfs_dev = &drive->gendev; g->flags = GENHD_FL_CD; if (ide_cdrom_setup(drive)) { struct cdrom_device_info *devinfo = &info->devinfo; diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 5b0c1ca8e75d..aecd9a7de7ed 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -1874,6 +1874,7 @@ static int idedisk_attach(ide_drive_t *drive) g->minors = 1 << PARTN_BITS; g->minor_shift = PARTN_BITS; g->de = drive->de; + g->driverfs_dev = &drive->gendev; g->flags = drive->removable ? GENHD_FL_REMOVABLE : 0; g->flags |= GENHD_FL_DEVFS; set_capacity(g, current_capacity(drive)); diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index fca1f92f896d..f10543ba3d8f 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -2110,6 +2110,7 @@ static int idefloppy_attach (ide_drive_t *drive) DRIVER(drive)->busy--; g->minors = 1 << PARTN_BITS; g->minor_shift = PARTN_BITS; + g->driverfs_dev = &drive->gendev; g->de = drive->de; g->flags = drive->removable ? GENHD_FL_REMOVABLE : 0; g->flags |= GENHD_FL_DEVFS; diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 05fe1b938eb4..39af5cce16f0 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -726,24 +726,6 @@ cleanup_dev: return 1; } -/* Driverfs file support */ -static ssize_t sr_device_kdev_read(struct device *driverfs_dev, - char *page, size_t count, loff_t off) -{ - kdev_t kdev; - kdev.value=(int)(long)driverfs_dev->driver_data; - return off ? 0 : sprintf(page, "%x\n",kdev.value); -} -static DEVICE_ATTR(kdev,S_IRUGO,sr_device_kdev_read,NULL); - -static ssize_t sr_device_type_read(struct device *driverfs_dev, - char *page, size_t count, loff_t off) -{ - return off ? 0 : sprintf (page, "CHR\n"); -} -static DEVICE_ATTR(type,S_IRUGO,sr_device_type_read,NULL); - - void sr_finish() { int i; @@ -797,22 +779,8 @@ void sr_finish() */ get_capabilities(cd); sr_vendor_init(cd); - - sprintf(cd->cdi.cdrom_driverfs_dev.bus_id, "%s:cd", - cd->device->sdev_driverfs_dev.bus_id); - sprintf(cd->cdi.cdrom_driverfs_dev.name, "%scdrom", - cd->device->sdev_driverfs_dev.name); - cd->cdi.cdrom_driverfs_dev.parent = - &cd->device->sdev_driverfs_dev; - cd->cdi.cdrom_driverfs_dev.bus = &scsi_driverfs_bus_type; - cd->cdi.cdrom_driverfs_dev.driver_data = - (void *)(long)__mkdev(MAJOR_NR, i); - device_register(&cd->cdi.cdrom_driverfs_dev); - device_create_file(&cd->cdi.cdrom_driverfs_dev, - &dev_attr_type); - device_create_file(&cd->cdi.cdrom_driverfs_dev, - &dev_attr_kdev); disk->de = cd->device->de; + disk->driverfs_dev = &cd->device->sdev_driverfs_dev; register_cdrom(&cd->cdi); set_capacity(disk, cd->capacity); add_disk(disk); diff --git a/fs/partitions/check.c b/fs/partitions/check.c index a61a83ded312..5fc23d047567 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -18,6 +18,7 @@ #include #include #include +#include <../drivers/base/fs/fs.h> /* Eeeeewwwww */ #include "check.h" @@ -111,57 +112,6 @@ char *disk_name(struct gendisk *hd, int part, char *buf) return buf; } -/* Driverfs file support */ -static ssize_t partition_device_kdev_read(struct device *driverfs_dev, - char *page, size_t count, loff_t off) -{ - kdev_t kdev; - kdev.value=(int)(long)driverfs_dev->driver_data; - return off ? 0 : sprintf (page, "%x\n",kdev.value); -} -static DEVICE_ATTR(kdev,S_IRUGO,partition_device_kdev_read,NULL); - -static ssize_t partition_device_type_read(struct device *driverfs_dev, - char *page, size_t count, loff_t off) -{ - return off ? 0 : sprintf (page, "BLK\n"); -} -static DEVICE_ATTR(type,S_IRUGO,partition_device_type_read,NULL); - -static void driverfs_create_partitions(struct gendisk *hd) -{ - struct device *parent = hd->driverfs_dev; - struct device *dev = &hd->disk_dev; - - /* if driverfs not supported by subsystem, skip partitions */ - if (!(hd->flags & GENHD_FL_DRIVERFS)) - return; - - if (parent) { - sprintf(dev->name, "%sdisc", parent->name); - sprintf(dev->bus_id, "%sdisc", parent->bus_id); - dev->parent = parent; - dev->bus = parent->bus; - } else { - sprintf(dev->name, "disc"); - sprintf(dev->bus_id, "disc"); - } - dev->driver_data = (void *)(long)__mkdev(hd->major, hd->first_minor); - device_register(dev); - device_create_file(dev, &dev_attr_type); - device_create_file(dev, &dev_attr_kdev); -} - -static void driverfs_remove_partitions(struct gendisk *hd) -{ - struct device *dev = &hd->disk_dev; - if (!(hd->flags & GENHD_FL_DRIVERFS)) - return; - device_remove_file(dev, &dev_attr_type); - device_remove_file(dev, &dev_attr_kdev); - put_device(dev); -} - static struct parsed_partitions * check_partition(struct gendisk *hd, struct block_device *bdev) { @@ -326,6 +276,40 @@ static void devfs_remove_partitions(struct gendisk *dev) #endif } +static ssize_t part_dev_read(struct device *dev, + char *page, size_t count, loff_t off) +{ + struct gendisk *disk = dev->parent->driver_data; + struct hd_struct *p = dev->driver_data; + int part = p - disk->part + 1; + dev_t base = MKDEV(disk->major, disk->first_minor); + return off ? 0 : sprintf(page, "%04x\n",base + part); +} +static ssize_t part_start_read(struct device *dev, + char *page, size_t count, loff_t off) +{ + struct hd_struct *p = dev->driver_data; + return off ? 0 : sprintf(page, "%llu\n",(u64)p->start_sect); +} +static ssize_t part_size_read(struct device *dev, + char *page, size_t count, loff_t off) +{ + struct hd_struct *p = dev->driver_data; + return off ? 0 : sprintf(page, "%llu\n",(u64)p->nr_sects); +} +static struct device_attribute part_attr_dev = { + .attr = {.name = "dev", .mode = S_IRUGO }, + .show = part_dev_read +}; +static struct device_attribute part_attr_start = { + .attr = {.name = "start", .mode = S_IRUGO }, + .show = part_start_read +}; +static struct device_attribute part_attr_size = { + .attr = {.name = "size", .mode = S_IRUGO }, + .show = part_size_read +}; + void delete_partition(struct gendisk *disk, int part) { struct hd_struct *p = disk->part + part - 1; @@ -338,8 +322,9 @@ void delete_partition(struct gendisk *disk, int part) dev = p->hd_driverfs_dev; p->hd_driverfs_dev = NULL; if (dev) { - device_remove_file(dev, &dev_attr_type); - device_remove_file(dev, &dev_attr_kdev); + device_remove_file(dev, &part_attr_size); + device_remove_file(dev, &part_attr_start); + device_remove_file(dev, &part_attr_dev); device_unregister(dev); } } @@ -352,43 +337,130 @@ static void part_release(struct device *dev) void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len) { struct hd_struct *p = disk->part + part - 1; - struct device *parent = disk->disk_dev.parent; + struct device *parent = &disk->disk_dev; struct device *dev; p->start_sect = start; p->nr_sects = len; devfs_register_partition(disk, part); - if (!(disk->flags & GENHD_FL_DRIVERFS)) - return; dev = kmalloc(sizeof(struct device), GFP_KERNEL); if (!dev) return; memset(dev, 0, sizeof(struct device)); - if (parent) { - sprintf(dev->name, "%spart%d", parent->name, part); - sprintf(dev->bus_id, "%s:p%d", parent->bus_id, part); - dev->parent = parent; - dev->bus = parent->bus; - } else { - sprintf(dev->name, "part%d", part); - sprintf(dev->bus_id, "p%d", part); - } + dev->parent = parent; + sprintf(dev->bus_id, "p%d", part); dev->release = part_release; - dev->driver_data = - (void *)(long)__mkdev(disk->major, disk->first_minor+part); + dev->driver_data = p; device_register(dev); - device_create_file(dev, &dev_attr_type); - device_create_file(dev, &dev_attr_kdev); + device_create_file(dev, &part_attr_dev); + device_create_file(dev, &part_attr_start); + device_create_file(dev, &part_attr_size); p->hd_driverfs_dev = dev; } +static ssize_t disk_dev_read(struct device *dev, + char *page, size_t count, loff_t off) +{ + struct gendisk *disk = dev->driver_data; + dev_t base = MKDEV(disk->major, disk->first_minor); + return off ? 0 : sprintf(page, "%04x\n",base); +} +static ssize_t disk_range_read(struct device *dev, + char *page, size_t count, loff_t off) +{ + struct gendisk *disk = dev->driver_data; + return off ? 0 : sprintf(page, "%d\n",disk->minors); +} +static ssize_t disk_size_read(struct device *dev, + char *page, size_t count, loff_t off) +{ + struct gendisk *disk = dev->driver_data; + return off ? 0 : sprintf(page, "%llu\n",(u64)get_capacity(disk)); +} +static struct device_attribute disk_attr_dev = { + .attr = {.name = "dev", .mode = S_IRUGO }, + .show = disk_dev_read +}; +static struct device_attribute disk_attr_range = { + .attr = {.name = "range", .mode = S_IRUGO }, + .show = disk_range_read +}; +static struct device_attribute disk_attr_size = { + .attr = {.name = "size", .mode = S_IRUGO }, + .show = disk_size_read +}; + +static void disk_driverfs_symlinks(struct gendisk *disk) +{ + struct device *target = disk->driverfs_dev; + struct device *dev = &disk->disk_dev; + struct device *p; + char *path; + char *s; + int length; + int depth; + + if (!target) + return; + + get_device(target); + + length = get_devpath_length(target); + length += strlen(".."); + + if (length > PATH_MAX) + return; + + if (!(path = kmalloc(length,GFP_KERNEL))) + return; + memset(path,0,length); + + /* our relative position */ + strcpy(path,".."); + + fill_devpath(target, path, length); + driverfs_create_symlink(&dev->dir, "device", path); + kfree(path); + + for (p = target, depth = 0; p; p = p->parent, depth++) + ; + length = get_devpath_length(dev); + length += 3 * depth - 1; + + if (length > PATH_MAX) + return; + + if (!(path = kmalloc(length,GFP_KERNEL))) + return; + memset(path,0,length); + for (s = path; depth--; s += 3) + strcpy(s, "../"); + + fill_devpath(dev, path, length); + driverfs_create_symlink(&target->dir, "block", path); + kfree(path); +} + /* Not exported, helper to add_disk(). */ void register_disk(struct gendisk *disk) { + struct device *dev = &disk->disk_dev; struct parsed_partitions *state; struct block_device *bdev; + char *s; int j; + strcpy(dev->bus_id, disk->disk_name); + /* ewww... some of these buggers have / in name... */ + s = strchr(dev->bus_id, '/'); + if (s) + *s = '!'; + device_add(dev); + device_create_file(dev, &disk_attr_dev); + device_create_file(dev, &disk_attr_range); + device_create_file(dev, &disk_attr_size); + disk_driverfs_symlinks(disk); + if (disk->flags & GENHD_FL_CD) devfs_create_cdrom(disk); @@ -404,7 +476,6 @@ void register_disk(struct gendisk *disk) if (blkdev_get(bdev, FMODE_READ, 0, BDEV_RAW) < 0) return; state = check_partition(disk, bdev); - driverfs_create_partitions(disk); devfs_create_partitions(disk); if (state) { for (j = 1; j < state->limit; j++) { @@ -499,8 +570,16 @@ void del_gendisk(struct gendisk *disk) disk->capacity = 0; disk->flags &= ~GENHD_FL_UP; unlink_gendisk(disk); - driverfs_remove_partitions(disk); devfs_remove_partitions(disk); + device_remove_file(&disk->disk_dev, &disk_attr_dev); + device_remove_file(&disk->disk_dev, &disk_attr_range); + device_remove_file(&disk->disk_dev, &disk_attr_size); + driverfs_remove_file(&disk->disk_dev.dir, "device"); + if (disk->driverfs_dev) { + driverfs_remove_file(&disk->driverfs_dev->dir, "block"); + put_device(disk->driverfs_dev); + } + device_del(&disk->disk_dev); } struct dev_name { diff --git a/include/linux/cdrom.h b/include/linux/cdrom.h index b287b7a24b11..4387203c95b7 100644 --- a/include/linux/cdrom.h +++ b/include/linux/cdrom.h @@ -730,7 +730,6 @@ struct cdrom_device_info { struct cdrom_device_ops *ops; /* link to device_ops */ struct cdrom_device_info *next; /* next device_info for this major */ void *handle; /* driver-dependent data */ - struct device cdrom_driverfs_dev; /* driverfs implementation */ /* specifications */ kdev_t dev; /* device number */ int mask; /* mask of capability: disables them */ -- cgit v1.2.3 From 68c16870dcfaba7c9e2dd5055a2caf4edcf42e87 Mon Sep 17 00:00:00 2001 From: Alexander Viro Date: Tue, 15 Oct 2002 04:25:32 -0700 Subject: [PATCH] refcounts for gendisks Finally. We use disk->dev.refcount as a gendisk refcount. New helper - get_disk(): atomic_inc on refcount. get_gendisk() does it on return, callers of get_gendisk() do put_disk() when they are done. --- drivers/block/genhd.c | 10 ++++++++++ drivers/block/ioctl.c | 47 +++++++++++++++++++++++++++++++++++++---------- fs/block_dev.c | 23 ++++++++++++++++++----- fs/partitions/check.c | 1 + include/linux/genhd.h | 7 +++++-- 5 files changed, 71 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index ecb2dcdf214d..1cc4655c04c9 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -100,6 +100,8 @@ get_gendisk(dev_t dev, int *part) read_lock(&gendisk_lock); if (gendisks[major].get) { disk = gendisks[major].get(minor); + if (disk) + get_disk(disk); read_unlock(&gendisk_lock); return disk; } @@ -109,6 +111,7 @@ get_gendisk(dev_t dev, int *part) continue; if (disk->first_minor + disk->minors <= minor) continue; + get_disk(disk); read_unlock(&gendisk_lock); *part = minor - disk->first_minor; return disk; @@ -244,6 +247,12 @@ struct gendisk *alloc_disk(int minors) return disk; } +struct gendisk *get_disk(struct gendisk *disk) +{ + atomic_inc(&disk->disk_dev.refcount); + return disk; +} + void put_disk(struct gendisk *disk) { if (disk) @@ -251,4 +260,5 @@ void put_disk(struct gendisk *disk) } EXPORT_SYMBOL(alloc_disk); +EXPORT_SYMBOL(get_disk); EXPORT_SYMBOL(put_disk); diff --git a/drivers/block/ioctl.c b/drivers/block/ioctl.c index 4af05bc32db2..de2da2b44cad 100644 --- a/drivers/block/ioctl.c +++ b/drivers/block/ioctl.c @@ -25,13 +25,17 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg) disk = get_gendisk(bdev->bd_dev, &part); if (!disk) return -ENXIO; - if (bdev != bdev->bd_contains) + if (bdev != bdev->bd_contains) { + put_disk(disk); return -EINVAL; + } if (part) BUG(); part = p.pno; - if (part <= 0 || part >= disk->minors) + if (part <= 0 || part >= disk->minors) { + put_disk(disk); return -EINVAL; + } switch (a.op) { case BLKPG_ADD_PARTITION: @@ -42,34 +46,46 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg) sizeof(long long) > sizeof(long)) { long pstart = start, plength = length; if (pstart != start || plength != length - || pstart < 0 || plength < 0) + || pstart < 0 || plength < 0) { + put_disk(disk); return -EINVAL; + } } /* partition number in use? */ - if (disk->part[part - 1].nr_sects != 0) + if (disk->part[part - 1].nr_sects != 0) { + put_disk(disk); return -EBUSY; + } /* overlap? */ for (i = 0; i < disk->minors - 1; i++) { struct hd_struct *s = &disk->part[i]; if (!(start+length <= s->start_sect || - start >= s->start_sect + s->nr_sects)) + start >= s->start_sect + s->nr_sects)) { + put_disk(disk); return -EBUSY; + } } /* all seems OK */ add_partition(disk, part, start, length); + put_disk(disk); return 0; case BLKPG_DEL_PARTITION: - if (disk->part[part - 1].nr_sects == 0) + if (disk->part[part - 1].nr_sects == 0) { + put_disk(disk); return -ENXIO; + } /* partition in use? Incomplete check for now. */ bdevp = bdget(MKDEV(disk->major, disk->first_minor) + part); - if (!bdevp) + if (!bdevp) { + put_disk(disk); return -ENOMEM; + } if (bd_claim(bdevp, &holder) < 0) { bdput(bdevp); + put_disk(disk); return -EBUSY; } @@ -80,8 +96,10 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg) delete_partition(disk, part); bd_release(bdevp); bdput(bdevp); + put_disk(disk); return 0; default: + put_disk(disk); return -EINVAL; } } @@ -92,16 +110,25 @@ static int blkdev_reread_part(struct block_device *bdev) struct gendisk *disk = get_gendisk(bdev->bd_dev, &part); int res = 0; - if (!disk || disk->minors == 1 || bdev != bdev->bd_contains) + if (!disk) return -EINVAL; + if (disk->minors == 1 || bdev != bdev->bd_contains) { + put_disk(disk); + return -EINVAL; + } if (part) BUG(); - if (!capable(CAP_SYS_ADMIN)) + if (!capable(CAP_SYS_ADMIN)) { + put_disk(disk); return -EACCES; - if (down_trylock(&bdev->bd_sem)) + } + if (down_trylock(&bdev->bd_sem)) { + put_disk(disk); return -EBUSY; + } res = rescan_partitions(disk, bdev); up(&bdev->bd_sem); + put_disk(disk); return res; } diff --git a/fs/block_dev.c b/fs/block_dev.c index dff0244e63a6..d029636b07e6 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -542,6 +542,7 @@ int check_disk_change(struct block_device *bdev) bdops->revalidate(dev); if (disk && disk->minors > 1) bdev->bd_invalidated = 1; + put_disk(disk); return 1; } @@ -553,7 +554,9 @@ int full_check_disk_change(struct block_device *bdev) BUG(); down(&bdev->bd_sem); if (check_disk_change(bdev)) { - rescan_partitions(get_gendisk(bdev->bd_dev, &n), bdev); + struct gendisk *disk = get_gendisk(bdev->bd_dev, &n); + rescan_partitions(disk, bdev); + put_disk(disk); res = 1; } up(&bdev->bd_sem); @@ -622,13 +625,18 @@ static int do_open(struct block_device *bdev, struct inode *inode, struct file * struct block_device *disk; disk = bdget(MKDEV(g->major, g->first_minor)); ret = -ENOMEM; - if (!disk) + if (!disk) { + put_disk(g); goto out1; + } ret = blkdev_get(disk, file->f_mode, file->f_flags, BDEV_RAW); - if (ret) + if (ret) { + put_disk(g); goto out1; + } bdev->bd_contains = disk; } + put_disk(g); } if (bdev->bd_contains == bdev) { int part; @@ -643,8 +651,10 @@ static int do_open(struct block_device *bdev, struct inode *inode, struct file * if (bdev->bd_op->open) { ret = bdev->bd_op->open(inode, file); - if (ret) + if (ret) { + put_disk(g); goto out2; + } } if (!bdev->bd_openers) { struct backing_dev_info *bdi; @@ -662,6 +672,7 @@ static int do_open(struct block_device *bdev, struct inode *inode, struct file * } if (bdev->bd_invalidated) rescan_partitions(g, bdev); + put_disk(g); } else { down(&bdev->bd_contains->bd_sem); bdev->bd_contains->bd_part_count++; @@ -673,15 +684,17 @@ static int do_open(struct block_device *bdev, struct inode *inode, struct file * inode->i_data.backing_dev_info = bdev->bd_inode->i_data.backing_dev_info = bdev->bd_contains->bd_inode->i_data.backing_dev_info; - if (!p->nr_sects) { + if (!(g->flags & GENHD_FL_UP) || !p->nr_sects) { bdev->bd_contains->bd_part_count--; up(&bdev->bd_contains->bd_sem); + put_disk(g); ret = -ENXIO; goto out2; } bdev->bd_queue = bdev->bd_contains->bd_queue; bdev->bd_offset = p->start_sect; bd_set_size(bdev, (loff_t) p->nr_sects << 9); + put_disk(g); } up(&bdev->bd_contains->bd_sem); } diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 5fc23d047567..e6ed1a443116 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -616,6 +616,7 @@ char *partition_name(dev_t dev) dname->name = NULL; if (hd) dname->name = disk_name(hd, part, dname->namebuf); + put_disk(hd); if (!dname->name) { sprintf(dname->namebuf, "[dev %s]", kdevname(to_kdev_t(dev))); dname->name = dname->namebuf; diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 6b859fad6a8a..030ee2f87891 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -266,6 +266,7 @@ extern void add_partition(struct gendisk *, int, sector_t, sector_t); extern void delete_partition(struct gendisk *, int); extern struct gendisk *alloc_disk(int minors); +extern struct gendisk *get_disk(struct gendisk *disk); extern void put_disk(struct gendisk *disk); /* will go away */ @@ -273,9 +274,11 @@ extern void blk_set_probe(int major, struct gendisk *(p)(int)); static inline unsigned int disk_index (kdev_t dev) { - int part; + int part, res; struct gendisk *g = get_gendisk(kdev_t_to_nr(dev), &part); - return g ? (minor(dev) >> g->minor_shift) : 0; + res = g ? (minor(dev) >> g->minor_shift) : 0; + put_disk(g); + return res; } #endif -- cgit v1.2.3 From 5682bcc620dbee99319997718c8929ec0d797854 Mon Sep 17 00:00:00 2001 From: Alexander Viro Date: Tue, 15 Oct 2002 04:25:37 -0700 Subject: [PATCH] bdev->bd_disk introduced There we go - now we can put a reference to gendisk into block_device. Which we do in do_open(). Most of the callers of get_gendisk() are simply using bdev->bd_disk now (and most of the put_disk() calls introduced on previous step disappear). We also put that pointer into struct request - ->rq_disk. That allows to get rid of disk_index() kludges in md.c (we simply count relevant IO in the struct gendisk fields) and kill the export of get_gendisk(). Notice that by now we can move _all_ IO counters into gendisk. That will kill a bunch of per-major arrays and more importantly, allow to merge sard in clean way. FWIW, we probably could show them as disk/partitions attributes in driverfs... --- drivers/block/genhd.c | 10 ++----- drivers/block/ioctl.c | 65 +++++++++--------------------------------- drivers/block/ll_rw_blk.c | 15 +++++++++- drivers/block/rd.c | 1 + drivers/md/md.c | 23 ++------------- fs/block_dev.c | 72 ++++++++++++++++++----------------------------- include/linux/blkdev.h | 1 + include/linux/fs.h | 1 + include/linux/genhd.h | 13 +++------ 9 files changed, 68 insertions(+), 133 deletions(-) (limited to 'include') diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index 1cc4655c04c9..449e69061bbc 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -61,10 +61,7 @@ void add_disk(struct gendisk *disk) { write_lock(&gendisk_lock); list_add(&disk->list, &gendisks[disk->major].list); - if (disk->minors > 1) - list_add_tail(&disk->full_list, &gendisk_list); - else - INIT_LIST_HEAD(&disk->full_list); + list_add_tail(&disk->full_list, &gendisk_list); write_unlock(&gendisk_lock); disk->flags |= GENHD_FL_UP; register_disk(disk); @@ -120,8 +117,6 @@ get_gendisk(dev_t dev, int *part) return NULL; } -EXPORT_SYMBOL(get_gendisk); - #ifdef CONFIG_PROC_FS /* iterator */ static void *part_start(struct seq_file *part, loff_t *pos) @@ -158,7 +153,7 @@ static int show_partition(struct seq_file *part, void *v) seq_puts(part, "major minor #blocks name\n\n"); /* Don't show non-partitionable devices or empty devices */ - if (!get_capacity(sgp)) + if (!get_capacity(sgp) || sgp->minors == 1) return 0; /* show the full disk and all non-0 size partitions of it */ @@ -239,6 +234,7 @@ struct gendisk *alloc_disk(int minors) disk->minors = minors; while (minors >>= 1) disk->minor_shift++; + INIT_LIST_HEAD(&disk->full_list); disk->disk_dev.bus = &disk_bus; disk->disk_dev.release = disk_release; disk->disk_dev.driver_data = disk; diff --git a/drivers/block/ioctl.c b/drivers/block/ioctl.c index de2da2b44cad..e420c691763d 100644 --- a/drivers/block/ioctl.c +++ b/drivers/block/ioctl.c @@ -22,21 +22,12 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg) return -EFAULT; if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition))) return -EFAULT; - disk = get_gendisk(bdev->bd_dev, &part); - if (!disk) - return -ENXIO; - if (bdev != bdev->bd_contains) { - put_disk(disk); + disk = bdev->bd_disk; + if (bdev != bdev->bd_contains) return -EINVAL; - } - if (part) - BUG(); part = p.pno; - if (part <= 0 || part >= disk->minors) { - put_disk(disk); + if (part <= 0 || part >= disk->minors) return -EINVAL; - } - switch (a.op) { case BLKPG_ADD_PARTITION: start = p.start >> 9; @@ -46,49 +37,33 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg) sizeof(long long) > sizeof(long)) { long pstart = start, plength = length; if (pstart != start || plength != length - || pstart < 0 || plength < 0) { - put_disk(disk); + || pstart < 0 || plength < 0) return -EINVAL; - } } - /* partition number in use? */ - if (disk->part[part - 1].nr_sects != 0) { - put_disk(disk); + if (disk->part[part - 1].nr_sects != 0) return -EBUSY; - } - /* overlap? */ for (i = 0; i < disk->minors - 1; i++) { struct hd_struct *s = &disk->part[i]; if (!(start+length <= s->start_sect || - start >= s->start_sect + s->nr_sects)) { - put_disk(disk); + start >= s->start_sect + s->nr_sects)) return -EBUSY; - } } /* all seems OK */ add_partition(disk, part, start, length); - put_disk(disk); return 0; case BLKPG_DEL_PARTITION: - if (disk->part[part - 1].nr_sects == 0) { - put_disk(disk); + if (disk->part[part - 1].nr_sects == 0) return -ENXIO; - } - /* partition in use? Incomplete check for now. */ bdevp = bdget(MKDEV(disk->major, disk->first_minor) + part); - if (!bdevp) { - put_disk(disk); + if (!bdevp) return -ENOMEM; - } if (bd_claim(bdevp, &holder) < 0) { bdput(bdevp); - put_disk(disk); return -EBUSY; } - /* all seems OK */ fsync_bdev(bdevp); invalidate_bdev(bdevp, 0); @@ -96,39 +71,25 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg *arg) delete_partition(disk, part); bd_release(bdevp); bdput(bdevp); - put_disk(disk); return 0; default: - put_disk(disk); return -EINVAL; } } static int blkdev_reread_part(struct block_device *bdev) { - int part; - struct gendisk *disk = get_gendisk(bdev->bd_dev, &part); - int res = 0; + struct gendisk *disk = bdev->bd_disk; + int res; - if (!disk) - return -EINVAL; - if (disk->minors == 1 || bdev != bdev->bd_contains) { - put_disk(disk); + if (disk->minors == 1 || bdev != bdev->bd_contains) return -EINVAL; - } - if (part) - BUG(); - if (!capable(CAP_SYS_ADMIN)) { - put_disk(disk); + if (!capable(CAP_SYS_ADMIN)) return -EACCES; - } - if (down_trylock(&bdev->bd_sem)) { - put_disk(disk); + if (down_trylock(&bdev->bd_sem)) return -EBUSY; - } res = rescan_partitions(disk, bdev); up(&bdev->bd_sem); - put_disk(disk); return res; } diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index ea56c1d8456c..eb877e50a8d1 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -1427,7 +1427,19 @@ void drive_stat_acct(struct request *rq, int nr_sectors, int new_io) int rw = rq_data_dir(rq); unsigned int index; - index = disk_index(rq->rq_dev); + if (!rq->rq_disk) + return; + + if (rw == READ) { + rq->rq_disk->rio += new_io; + rq->rq_disk->reads += nr_sectors; + } else if (rw == WRITE) { + rq->rq_disk->wio += new_io; + rq->rq_disk->writes += nr_sectors; + } + + index = rq->rq_disk->first_minor >> rq->rq_disk->minor_shift; + if ((index >= DK_MAX_DISK) || (major >= DK_MAX_MAJOR)) return; @@ -1747,6 +1759,7 @@ get_rq: req->waiting = NULL; req->bio = req->biotail = bio; req->rq_dev = to_kdev_t(bio->bi_bdev->bd_dev); + req->rq_disk = bio->bi_bdev->bd_disk; add_request(q, req, insert_here); out: if (freereq) diff --git a/drivers/block/rd.c b/drivers/block/rd.c index 7d72b786080c..bbd247fa29dc 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -381,6 +381,7 @@ static int rd_open(struct inode * inode, struct file * filp) rd_bdev[unit]->bd_inode->i_mapping->a_ops = &ramdisk_aops; rd_bdev[unit]->bd_inode->i_size = rd_length[unit]; rd_bdev[unit]->bd_queue = &blk_dev[MAJOR_NR].request_queue; + rd_bdev[unit]->bd_disk = get_disk(rd_disks[unit]); } return 0; diff --git a/drivers/md/md.c b/drivers/md/md.c index 205bb0fdeee0..784e3b69213e 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -2731,18 +2731,9 @@ int unregister_md_personality(int pnum) return 0; } -static unsigned int sync_io[DK_MAX_MAJOR][DK_MAX_DISK]; void md_sync_acct(mdk_rdev_t *rdev, unsigned long nr_sectors) { - kdev_t dev = to_kdev_t(rdev->bdev->bd_dev); - unsigned int major = major(dev); - unsigned int index; - - index = disk_index(dev); - if ((index >= DK_MAX_DISK) || (major >= DK_MAX_MAJOR)) - return; - - sync_io[major][index] += nr_sectors; + rdev->bdev->bd_disk->sync_io += nr_sectors; } static int is_mddev_idle(mddev_t *mddev) @@ -2754,16 +2745,8 @@ static int is_mddev_idle(mddev_t *mddev) idle = 1; ITERATE_RDEV(mddev,rdev,tmp) { - kdev_t dev = to_kdev_t(rdev->bdev->bd_dev); - int major = major(dev); - int idx = disk_index(dev); - - if ((idx >= DK_MAX_DISK) || (major >= DK_MAX_MAJOR)) - continue; - - curr_events = kstat.dk_drive_rblk[major][idx] + - kstat.dk_drive_wblk[major][idx] ; - curr_events -= sync_io[major][idx]; + struct gendisk *disk = rdev->bdev->bd_disk; + curr_events = disk->reads + disk->writes - disk->sync_io; if ((curr_events - rdev->last_events) > 32) { rdev->last_events = curr_events; idle = 0; diff --git a/fs/block_dev.c b/fs/block_dev.c index d029636b07e6..1ad7f467993b 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -526,8 +526,6 @@ int check_disk_change(struct block_device *bdev) { struct block_device_operations * bdops = bdev->bd_op; kdev_t dev = to_kdev_t(bdev->bd_dev); - struct gendisk *disk; - int part; if (bdops->check_media_change == NULL) return 0; @@ -537,26 +535,21 @@ int check_disk_change(struct block_device *bdev) if (invalidate_device(dev, 0)) printk("VFS: busy inodes on changed media.\n"); - disk = get_gendisk(bdev->bd_dev, &part); if (bdops->revalidate) bdops->revalidate(dev); - if (disk && disk->minors > 1) + if (bdev->bd_disk->minors > 1) bdev->bd_invalidated = 1; - put_disk(disk); return 1; } int full_check_disk_change(struct block_device *bdev) { int res = 0; - int n; if (bdev->bd_contains != bdev) BUG(); down(&bdev->bd_sem); if (check_disk_change(bdev)) { - struct gendisk *disk = get_gendisk(bdev->bd_dev, &n); - rescan_partitions(disk, bdev); - put_disk(disk); + rescan_partitions(bdev->bd_disk, bdev); res = 1; } up(&bdev->bd_sem); @@ -598,6 +591,8 @@ static int do_open(struct block_device *bdev, struct inode *inode, struct file * kdev_t dev = to_kdev_t(bdev->bd_dev); struct module *owner = NULL; struct block_device_operations *ops, *old; + struct gendisk *disk; + int part; lock_kernel(); ops = get_blkfops(major(dev)); @@ -617,53 +612,41 @@ static int do_open(struct block_device *bdev, struct inode *inode, struct file * if (owner) __MOD_DEC_USE_COUNT(owner); } + disk = get_gendisk(bdev->bd_dev, &part); + if (!disk) + goto out1; if (!bdev->bd_contains) { - int part; - struct gendisk *g = get_gendisk(bdev->bd_dev, &part); bdev->bd_contains = bdev; - if (g && part) { - struct block_device *disk; - disk = bdget(MKDEV(g->major, g->first_minor)); + if (part) { + struct block_device *whole; + whole = bdget(MKDEV(disk->major, disk->first_minor)); ret = -ENOMEM; - if (!disk) { - put_disk(g); + if (!whole) goto out1; - } - ret = blkdev_get(disk, file->f_mode, file->f_flags, BDEV_RAW); - if (ret) { - put_disk(g); + ret = blkdev_get(whole, file->f_mode, file->f_flags, BDEV_RAW); + if (ret) goto out1; - } - bdev->bd_contains = disk; + bdev->bd_contains = whole; } - put_disk(g); } if (bdev->bd_contains == bdev) { - int part; - struct gendisk *g = get_gendisk(bdev->bd_dev, &part); - + if (!bdev->bd_openers) + bdev->bd_disk = disk; if (!bdev->bd_queue) { struct blk_dev_struct *p = blk_dev + major(dev); bdev->bd_queue = &p->request_queue; if (p->queue) bdev->bd_queue = p->queue(dev); } - if (bdev->bd_op->open) { ret = bdev->bd_op->open(inode, file); - if (ret) { - put_disk(g); + if (ret) goto out2; - } } if (!bdev->bd_openers) { struct backing_dev_info *bdi; - sector_t sect = 0; - bdev->bd_offset = 0; - if (g) - sect = get_capacity(g); - bd_set_size(bdev, (loff_t)sect << 9); + bd_set_size(bdev, (loff_t)get_capacity(disk) << 9); bdi = blk_get_backing_dev_info(bdev); if (bdi == NULL) bdi = &default_backing_dev_info; @@ -671,34 +654,31 @@ static int do_open(struct block_device *bdev, struct inode *inode, struct file * bdev->bd_inode->i_data.backing_dev_info = bdi; } if (bdev->bd_invalidated) - rescan_partitions(g, bdev); - put_disk(g); + rescan_partitions(disk, bdev); } else { down(&bdev->bd_contains->bd_sem); bdev->bd_contains->bd_part_count++; if (!bdev->bd_openers) { - int part; - struct gendisk *g = get_gendisk(bdev->bd_dev, &part); struct hd_struct *p; - p = g->part + part - 1; + p = disk->part + part - 1; inode->i_data.backing_dev_info = bdev->bd_inode->i_data.backing_dev_info = bdev->bd_contains->bd_inode->i_data.backing_dev_info; - if (!(g->flags & GENHD_FL_UP) || !p->nr_sects) { + if (!(disk->flags & GENHD_FL_UP) || !p->nr_sects) { bdev->bd_contains->bd_part_count--; up(&bdev->bd_contains->bd_sem); - put_disk(g); ret = -ENXIO; goto out2; } bdev->bd_queue = bdev->bd_contains->bd_queue; bdev->bd_offset = p->start_sect; bd_set_size(bdev, (loff_t) p->nr_sects << 9); - put_disk(g); + bdev->bd_disk = disk; } up(&bdev->bd_contains->bd_sem); } - bdev->bd_openers++; + if (bdev->bd_openers++) + put_disk(disk); up(&bdev->bd_sem); unlock_kernel(); return 0; @@ -712,6 +692,7 @@ out2: } } out1: + put_disk(disk); if (!old) { bdev->bd_op = NULL; if (owner) @@ -785,15 +766,18 @@ int blkdev_put(struct block_device *bdev, int kind) up(&bdev->bd_contains->bd_sem); } if (!bdev->bd_openers) { + struct gendisk *disk = bdev->bd_disk; if (bdev->bd_op->owner) __MOD_DEC_USE_COUNT(bdev->bd_op->owner); bdev->bd_op = NULL; bdev->bd_queue = NULL; + bdev->bd_disk = NULL; bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info; if (bdev != bdev->bd_contains) { blkdev_put(bdev->bd_contains, BDEV_RAW); bdev->bd_contains = NULL; } + put_disk(disk); } unlock_kernel(); up(&bdev->bd_sem); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 607641c6cfb1..ccb56d58de6a 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -34,6 +34,7 @@ struct request { int rq_status; /* should split this into a few status bits */ kdev_t rq_dev; + struct gendisk *rq_disk; int errors; sector_t sector; unsigned long nr_sectors; diff --git a/include/linux/fs.h b/include/linux/fs.h index cac13f931cec..bca164f4265a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -359,6 +359,7 @@ struct block_device { sector_t bd_offset; unsigned bd_part_count; int bd_invalidated; + struct gendisk * bd_disk; }; struct inode { diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 030ee2f87891..9de2f51ae935 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -90,6 +90,10 @@ struct gendisk { devfs_handle_t disk_de; /* piled higher and deeper */ struct device *driverfs_dev; struct device disk_dev; + + unsigned sync_io; /* RAID */ + unsigned reads, writes; + unsigned rio, wio; }; /* drivers/block/genhd.c */ @@ -272,15 +276,6 @@ extern void put_disk(struct gendisk *disk); /* will go away */ extern void blk_set_probe(int major, struct gendisk *(p)(int)); -static inline unsigned int disk_index (kdev_t dev) -{ - int part, res; - struct gendisk *g = get_gendisk(kdev_t_to_nr(dev), &part); - res = g ? (minor(dev) >> g->minor_shift) : 0; - put_disk(g); - return res; -} - #endif #endif -- cgit v1.2.3 From 19bb2ab92d37ae85fc6d4cb6ae1fea6a8de027b1 Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 15 Oct 2002 04:30:26 -0700 Subject: [PATCH] oprofile - hooks This implements the simple hooks we need to catch unmappings, and to make sure no stale task_struct*'s are ever used by the main oprofile core mechanism. If disabled, it compiles to nothing. --- arch/i386/Config.help | 5 +++ arch/i386/config.in | 7 ++++ include/linux/profile.h | 56 ++++++++++++++++++++++++++++++ kernel/Makefile | 5 +-- kernel/exit.c | 10 ++++-- kernel/profile.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++ mm/mmap.c | 8 +++++ 7 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 include/linux/profile.h create mode 100644 kernel/profile.c (limited to 'include') diff --git a/arch/i386/Config.help b/arch/i386/Config.help index 299dea0c6536..d6f3cdc95f05 100644 --- a/arch/i386/Config.help +++ b/arch/i386/Config.help @@ -1048,6 +1048,11 @@ CONFIG_DEBUG_OBSOLETE Say Y here if you want to reduce the chances of the tree compiling, and are prepared to dig into driver internals to fix compile errors. +Profiling support +CONFIG_PROFILING + Say Y here to enable the extended profiling support mechanisms used + by profilers such as OProfile. + Software Suspend CONFIG_SOFTWARE_SUSPEND Enable the possibilty of suspendig machine. It doesn't need APM. diff --git a/arch/i386/config.in b/arch/i386/config.in index 784e35d23bce..97a9b862d72f 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -442,6 +442,13 @@ source drivers/usb/Config.in source net/bluetooth/Config.in +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + mainmenu_option next_comment + comment 'Profiling support' + bool 'Profiling support (EXPERIMENTAL)' CONFIG_PROFILING + endmenu +fi + mainmenu_option next_comment comment 'Kernel hacking' if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then diff --git a/include/linux/profile.h b/include/linux/profile.h new file mode 100644 index 000000000000..15c1e91198b0 --- /dev/null +++ b/include/linux/profile.h @@ -0,0 +1,56 @@ +#ifndef _LINUX_PROFILE_H +#define _LINUX_PROFILE_H + +#ifdef __KERNEL__ + +#include +#include +#include +#include + +enum profile_type { + EXIT_TASK, + EXIT_MMAP, + EXEC_UNMAP +}; + +#ifdef CONFIG_PROFILING + +struct notifier_block; +struct task_struct; +struct mm_struct; + +/* task is in do_exit() */ +void profile_exit_task(struct task_struct * task); + +/* change of vma mappings */ +void profile_exec_unmap(struct mm_struct * mm); + +/* exit of all vmas for a task */ +void profile_exit_mmap(struct mm_struct * mm); + +int profile_event_register(enum profile_type, struct notifier_block * n); + +int profile_event_unregister(enum profile_type, struct notifier_block * n); + +#else + +static inline int profile_event_register(enum profile_type t, struct notifier_block * n) +{ + return -ENOSYS; +} + +static inline int profile_event_unregister(enum profile_type t, struct notifier_block * n) +{ + return -ENOSYS; +} + +#define profile_exit_task(a) do { } while (0) +#define profile_exec_unmap(a) do { } while (0) +#define profile_exit_mmap(a) do { } while (0) + +#endif /* CONFIG_PROFILING */ + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_PROFILE_H */ diff --git a/kernel/Makefile b/kernel/Makefile index b3fce6d3ac9c..8e18771791de 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -3,9 +3,10 @@ # export-objs = signal.o sys.o kmod.o workqueue.o ksyms.o pm.o exec_domain.o \ - printk.o platform.o suspend.o dma.o module.o cpufreq.o + printk.o platform.o suspend.o dma.o module.o cpufreq.o \ + profile.o -obj-y = sched.o fork.o exec_domain.o panic.o printk.o \ +obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \ module.o exit.o itimer.o time.o softirq.o resource.o \ sysctl.o capability.o ptrace.o timer.o user.o \ signal.o sys.o kmod.o workqueue.o futex.o platform.o pid.o diff --git a/kernel/exit.c b/kernel/exit.c index 6ed07def4c62..c2b0f6eeff0f 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -59,11 +60,12 @@ void release_task(struct task_struct * p) { struct dentry *proc_dentry; task_t *leader; - - if (p->state < TASK_ZOMBIE) - BUG(); + + BUG_ON(p->state < TASK_ZOMBIE); + if (p != current) wait_task_inactive(p); + atomic_dec(&p->user->processes); security_ops->task_free_security(p); free_uid(p->user); @@ -635,6 +637,8 @@ NORET_TYPE void do_exit(long code) current->comm, current->pid, preempt_count()); + profile_exit_task(tsk); + fake_volatile: acct_process(code); __exit_mm(tsk); diff --git a/kernel/profile.c b/kernel/profile.c new file mode 100644 index 000000000000..7ebffe971ca8 --- /dev/null +++ b/kernel/profile.c @@ -0,0 +1,91 @@ +/* + * linux/kernel/profile.c + */ + +#include +#include +#include +#include +#include +#include + +/* Profile event notifications */ + +#ifdef CONFIG_PROFILING + +static DECLARE_RWSEM(profile_rwsem); +static struct notifier_block * exit_task_notifier; +static struct notifier_block * exit_mmap_notifier; +static struct notifier_block * exec_unmap_notifier; + +void profile_exit_task(struct task_struct * task) +{ + down_read(&profile_rwsem); + notifier_call_chain(&exit_task_notifier, 0, task); + up_read(&profile_rwsem); +} + +void profile_exit_mmap(struct mm_struct * mm) +{ + down_read(&profile_rwsem); + notifier_call_chain(&exit_mmap_notifier, 0, mm); + up_read(&profile_rwsem); +} + +void profile_exec_unmap(struct mm_struct * mm) +{ + down_read(&profile_rwsem); + notifier_call_chain(&exec_unmap_notifier, 0, mm); + up_read(&profile_rwsem); +} + +int profile_event_register(enum profile_type type, struct notifier_block * n) +{ + int err = -EINVAL; + + down_write(&profile_rwsem); + + switch (type) { + case EXIT_TASK: + err = notifier_chain_register(&exit_task_notifier, n); + break; + case EXIT_MMAP: + err = notifier_chain_register(&exit_mmap_notifier, n); + break; + case EXEC_UNMAP: + err = notifier_chain_register(&exec_unmap_notifier, n); + break; + } + + up_write(&profile_rwsem); + + return err; +} + + +int profile_event_unregister(enum profile_type type, struct notifier_block * n) +{ + int err = -EINVAL; + + down_write(&profile_rwsem); + + switch (type) { + case EXIT_TASK: + err = notifier_chain_unregister(&exit_task_notifier, n); + break; + case EXIT_MMAP: + err = notifier_chain_unregister(&exit_mmap_notifier, n); + break; + case EXEC_UNMAP: + err = notifier_chain_unregister(&exec_unmap_notifier, n); + break; + } + + up_write(&profile_rwsem); + return err; +} + +#endif /* CONFIG_PROFILING */ + +EXPORT_SYMBOL_GPL(profile_event_register); +EXPORT_SYMBOL_GPL(profile_event_unregister); diff --git a/mm/mmap.c b/mm/mmap.c index 5d43e84413b1..90ae8b22ab96 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -1104,6 +1105,10 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len) if (mpnt->vm_start >= end) return 0; + /* Something will probably happen, so notify. */ + if (mpnt->vm_file && (mpnt->vm_flags & VM_EXEC)) + profile_exec_unmap(mm); + /* * If we need to split any vma, do it now to save pain later. */ @@ -1253,7 +1258,10 @@ void exit_mmap(struct mm_struct * mm) mmu_gather_t *tlb; struct vm_area_struct * mpnt; + profile_exit_mmap(mm); + release_segments(mm); + spin_lock(&mm->page_table_lock); tlb = tlb_gather_mmu(mm, 1); -- cgit v1.2.3 From 7e1aee05c99cfbb7e5cf33bae11ab9fa8df6c57c Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 15 Oct 2002 04:30:32 -0700 Subject: [PATCH] oprofile - dcookies This implements the persistent path-to-dcookies mapping, and adds a system call for the user-space profiler to look up the profile data, so it can tag profiles to specific binaries. --- arch/i386/kernel/entry.S | 1 + fs/Makefile | 4 +- fs/dcache.c | 1 + fs/dcookies.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++ include/asm-i386/unistd.h | 2 + include/linux/dcache.h | 3 + include/linux/dcookies.h | 69 ++++++++++ kernel/sys.c | 2 + 8 files changed, 404 insertions(+), 1 deletion(-) create mode 100644 fs/dcookies.c create mode 100644 include/linux/dcookies.h (limited to 'include') diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index 557b684431c5..e873703e0c34 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -736,6 +736,7 @@ ENTRY(sys_call_table) .long sys_alloc_hugepages /* 250 */ .long sys_free_hugepages .long sys_exit_group + .long sys_lookup_dcookie .rept NR_syscalls-(.-sys_call_table)/4 .long sys_ni_syscall diff --git a/fs/Makefile b/fs/Makefile index d902bdd8bda3..a4320cf860ac 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -6,7 +6,7 @@ # export-objs := open.o dcache.o buffer.o bio.o inode.o dquot.o mpage.o aio.o \ - fcntl.o read_write.o + fcntl.o read_write.o dcookies.o obj-y := open.o read_write.o devices.o file_table.o buffer.o \ bio.o super.o block_dev.o char_dev.o stat.o exec.o pipe.o \ @@ -40,6 +40,8 @@ obj-y += partitions/ obj-y += driverfs/ obj-y += devpts/ +obj-$(CONFIG_PROFILING) += dcookies.o + # Do not add any filesystems before this line obj-$(CONFIG_EXT3_FS) += ext3/ # Before ext2 so root fs can be ext3 obj-$(CONFIG_JBD) += jbd/ diff --git a/fs/dcache.c b/fs/dcache.c index ef0871dbcdb2..d0fcfeba16ee 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -637,6 +637,7 @@ struct dentry * d_alloc(struct dentry * parent, const struct qstr *name) dentry->d_op = NULL; dentry->d_fsdata = NULL; dentry->d_mounted = 0; + dentry->d_cookie = NULL; INIT_LIST_HEAD(&dentry->d_hash); INIT_LIST_HEAD(&dentry->d_lru); INIT_LIST_HEAD(&dentry->d_subdirs); diff --git a/fs/dcookies.c b/fs/dcookies.c new file mode 100644 index 000000000000..0236c146b451 --- /dev/null +++ b/fs/dcookies.c @@ -0,0 +1,323 @@ +/* + * dcookies.c + * + * Copyright 2002 John Levon + * + * Persistent cookie-path mappings. These are used by + * profilers to convert a per-task EIP value into something + * non-transitory that can be processed at a later date. + * This is done by locking the dentry/vfsmnt pair in the + * kernel until released by the tasks needing the persistent + * objects. The tag is simply an unsigned long that refers + * to the pair and can be looked up from userspace. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The dcookies are allocated from a kmem_cache and + * hashed onto a small number of lists. None of the + * code here is particularly performance critical + */ +struct dcookie_struct { + struct dentry * dentry; + struct vfsmount * vfsmnt; + struct list_head hash_list; +}; + +static LIST_HEAD(dcookie_users); +static DECLARE_MUTEX(dcookie_sem); +static kmem_cache_t * dcookie_cache; +static struct list_head * dcookie_hashtable; +static size_t hash_size; + +static inline int is_live(void) +{ + return !(list_empty(&dcookie_users)); +} + + +/* The dentry is locked, its address will do for the cookie */ +static inline unsigned long dcookie_value(struct dcookie_struct * dcs) +{ + return (unsigned long)dcs->dentry; +} + + +static size_t dcookie_hash(unsigned long dcookie) +{ + return (dcookie >> 2) & (hash_size - 1); +} + + +static struct dcookie_struct * find_dcookie(unsigned long dcookie) +{ + struct dcookie_struct * found = 0; + struct dcookie_struct * dcs; + struct list_head * pos; + struct list_head * list; + + list = dcookie_hashtable + dcookie_hash(dcookie); + + list_for_each(pos, list) { + dcs = list_entry(pos, struct dcookie_struct, hash_list); + if (dcookie_value(dcs) == dcookie) { + found = dcs; + break; + } + } + + return found; +} + + +static void hash_dcookie(struct dcookie_struct * dcs) +{ + struct list_head * list = dcookie_hashtable + dcookie_hash(dcookie_value(dcs)); + list_add(&dcs->hash_list, list); +} + + +static struct dcookie_struct * alloc_dcookie(struct dentry * dentry, + struct vfsmount * vfsmnt) +{ + struct dcookie_struct * dcs = kmem_cache_alloc(dcookie_cache, GFP_KERNEL); + if (!dcs) + return NULL; + + atomic_inc(&dentry->d_count); + atomic_inc(&vfsmnt->mnt_count); + dentry->d_cookie = dcs; + + dcs->dentry = dentry; + dcs->vfsmnt = vfsmnt; + hash_dcookie(dcs); + + return dcs; +} + + +/* This is the main kernel-side routine that retrieves the cookie + * value for a dentry/vfsmnt pair. + */ +int get_dcookie(struct dentry * dentry, struct vfsmount * vfsmnt, + unsigned long * cookie) +{ + int err = 0; + struct dcookie_struct * dcs; + + down(&dcookie_sem); + + if (!is_live()) { + err = -EINVAL; + goto out; + } + + dcs = dentry->d_cookie; + + if (!dcs) + dcs = alloc_dcookie(dentry, vfsmnt); + + if (!dcs) { + err = -ENOMEM; + goto out; + } + + *cookie = dcookie_value(dcs); + +out: + up(&dcookie_sem); + return err; +} + + +/* And here is where the userspace process can look up the cookie value + * to retrieve the path. + */ +asmlinkage int sys_lookup_dcookie(unsigned long cookie, char * buf, size_t len) +{ + char * kbuf; + char * path; + int err = -EINVAL; + size_t pathlen; + struct dcookie_struct * dcs; + + /* we could leak path information to users + * without dir read permission without this + */ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + down(&dcookie_sem); + + if (!is_live()) { + err = -EINVAL; + goto out; + } + + if (!(dcs = find_dcookie(cookie))) + goto out; + + err = -ENOMEM; + kbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!kbuf) + goto out; + memset(kbuf, 0, PAGE_SIZE); + + /* FIXME: (deleted) ? */ + path = d_path(dcs->dentry, dcs->vfsmnt, kbuf, PAGE_SIZE); + + err = 0; + + pathlen = kbuf + PAGE_SIZE - path; + if (len > pathlen) + len = pathlen; + + if (copy_to_user(buf, path, len)) + err = -EFAULT; + + kfree(kbuf); +out: + up(&dcookie_sem); + return err; +} + + +static int dcookie_init(void) +{ + struct list_head * d; + unsigned int i, hash_bits; + int err = -ENOMEM; + + dcookie_cache = kmem_cache_create("dcookie_cache", + sizeof(struct dcookie_struct), + 0, 0, NULL, NULL); + + if (!dcookie_cache) + goto out; + + dcookie_hashtable = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!dcookie_hashtable) + goto out_kmem; + + err = 0; + + /* + * Find the power-of-two list-heads that can fit into the allocation.. + * We don't guarantee that "sizeof(struct list_head)" is necessarily + * a power-of-two. + */ + hash_size = PAGE_SIZE / sizeof(struct list_head); + hash_bits = 0; + do { + hash_bits++; + } while ((hash_size >> hash_bits) != 0); + hash_bits--; + + /* + * Re-calculate the actual number of entries and the mask + * from the number of bits we can fit. + */ + hash_size = 1UL << hash_bits; + + /* And initialize the newly allocated array */ + d = dcookie_hashtable; + i = hash_size; + do { + INIT_LIST_HEAD(d); + d++; + i--; + } while (i); + +out: + return err; +out_kmem: + kmem_cache_destroy(dcookie_cache); + goto out; +} + + +static void free_dcookie(struct dcookie_struct * dcs) +{ + dcs->dentry->d_cookie = NULL; + dput(dcs->dentry); + mntput(dcs->vfsmnt); + kmem_cache_free(dcookie_cache, dcs); +} + + +static void dcookie_exit(void) +{ + struct list_head * list; + struct list_head * pos; + struct list_head * pos2; + struct dcookie_struct * dcs; + size_t i; + + for (i = 0; i < hash_size; ++i) { + list = dcookie_hashtable + i; + list_for_each_safe(pos, pos2, list) { + dcs = list_entry(pos, struct dcookie_struct, hash_list); + list_del(&dcs->hash_list); + free_dcookie(dcs); + } + } + + kfree(dcookie_hashtable); + kmem_cache_destroy(dcookie_cache); +} + + +struct dcookie_user { + struct list_head next; +}; + +struct dcookie_user * dcookie_register(void) +{ + struct dcookie_user * user; + + down(&dcookie_sem); + + user = kmalloc(sizeof(struct dcookie_user), GFP_KERNEL); + if (!user) + goto out; + + if (!is_live() && dcookie_init()) + goto out_free; + + list_add(&user->next, &dcookie_users); + +out: + up(&dcookie_sem); + return user; +out_free: + kfree(user); + user = NULL; + goto out; +} + + +void dcookie_unregister(struct dcookie_user * user) +{ + down(&dcookie_sem); + + list_del(&user->next); + kfree(user); + + if (!is_live()) + dcookie_exit(); + + up(&dcookie_sem); +} + +EXPORT_SYMBOL_GPL(dcookie_register); +EXPORT_SYMBOL_GPL(dcookie_unregister); +EXPORT_SYMBOL_GPL(get_dcookie); diff --git a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h index 8765a0f82aff..159dfa7fefe1 100644 --- a/include/asm-i386/unistd.h +++ b/include/asm-i386/unistd.h @@ -257,6 +257,8 @@ #define __NR_alloc_hugepages 250 #define __NR_free_hugepages 251 #define __NR_exit_group 252 +#define __NR_lookup_dcookie 253 + /* user-visible error numbers are in the range -1 - -124: see */ diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 71708edafce9..76a5085043e1 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -66,6 +66,8 @@ static __inline__ unsigned int full_name_hash(const unsigned char * name, unsign #define DNAME_INLINE_LEN 16 +struct dcookie_struct; + struct dentry { atomic_t d_count; unsigned int d_flags; @@ -84,6 +86,7 @@ struct dentry { unsigned long d_vfs_flags; void * d_fsdata; /* fs-specific data */ unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */ + struct dcookie_struct * d_cookie; /* cookie, if any */ }; struct dentry_operations { diff --git a/include/linux/dcookies.h b/include/linux/dcookies.h new file mode 100644 index 000000000000..b2ae9692dc05 --- /dev/null +++ b/include/linux/dcookies.h @@ -0,0 +1,69 @@ +/* + * dcookies.h + * + * Persistent cookie-path mappings + * + * Copyright 2002 John Levon + */ + +#ifndef DCOOKIES_H +#define DCOOKIES_H + +#include + +#ifdef CONFIG_PROFILING + +#include + +struct dcookie_user; + +/** + * dcookie_register - register a user of dcookies + * + * Register as a dcookie user. Returns %NULL on failure. + */ +struct dcookie_user * dcookie_register(void); + +/** + * dcookie_unregister - unregister a user of dcookies + * + * Unregister as a dcookie user. This may invalidate + * any dcookie values returned from get_dcookie(). + */ +void dcookie_unregister(struct dcookie_user * user); + +/** + * get_dcookie - acquire a dcookie + * + * Convert the given dentry/vfsmount pair into + * a cookie value. + * + * Returns -EINVAL if no living task has registered as a + * dcookie user. + * + * Returns 0 on success, with *cookie filled in + */ +int get_dcookie(struct dentry * dentry, struct vfsmount * vfsmnt, + unsigned long * cookie); + +#else + +struct dcookie_user * dcookie_register(void) +{ + return 0; +} + +void dcookie_unregister(struct dcookie_user * user) +{ + return; +} + +static inline int get_dcookie(struct dentry * dentry, + struct vfsmount * vfsmnt, unsigned long * cookie) +{ + return -ENOSYS; +} + +#endif /* CONFIG_PROFILING */ + +#endif /* DCOOKIES_H */ diff --git a/kernel/sys.c b/kernel/sys.c index 5b7e84384cfa..3c2992ac68f2 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -202,6 +203,7 @@ asmlinkage long sys_ni_syscall(void) cond_syscall(sys_nfsservctl) cond_syscall(sys_quotactl) cond_syscall(sys_acct) +cond_syscall(sys_lookup_dcookie) static int set_one_prio(struct task_struct *p, int niceval, int error) { -- cgit v1.2.3 From 120790b8fe2d901d99f459a567fefbb35c2d15e1 Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 15 Oct 2002 04:30:38 -0700 Subject: [PATCH] oprofile - timer hook This implements a simple hook into the profiling timer for x86 so that non-perfctr machines can still use oprofile. This has proven useful for laptops and the like. It also reduces header dependencies a bit by centralising readprofile code --- arch/i386/kernel/Makefile | 1 + arch/i386/kernel/apic.c | 12 ++-------- arch/i386/kernel/i386_ksyms.c | 3 +++ arch/i386/kernel/profile.c | 45 +++++++++++++++++++++++++++++++++++ arch/i386/kernel/time.c | 5 ---- arch/i386/mach-generic/do_timer.h | 3 +-- arch/i386/mach-visws/do_timer.h | 3 +-- fs/proc/proc_misc.c | 1 + include/asm-i386/hw_irq.h | 49 ++++++++++++++++++++++++++++++++------- include/linux/profile.h | 11 +++++++++ include/linux/sched.h | 4 ---- init/main.c | 20 ++-------------- kernel/profile.c | 30 ++++++++++++++++++++++++ kernel/timer.c | 4 ---- 14 files changed, 138 insertions(+), 53 deletions(-) create mode 100644 arch/i386/kernel/profile.c (limited to 'include') diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile index d201c60ac5c2..55f9312b7f39 100644 --- a/arch/i386/kernel/Makefile +++ b/arch/i386/kernel/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_X86_LOCAL_APIC) += apic.o nmi.o obj-$(CONFIG_X86_IO_APIC) += io_apic.o obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend.o obj-$(CONFIG_X86_NUMAQ) += numaq.o +obj-$(CONFIG_PROFILING) += profile.o EXTRA_AFLAGS := -traditional diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c index c2f56438f749..bff34a4d1dcf 100644 --- a/arch/i386/kernel/apic.c +++ b/arch/i386/kernel/apic.c @@ -1008,17 +1008,9 @@ int setup_profiling_timer(unsigned int multiplier) inline void smp_local_timer_interrupt(struct pt_regs * regs) { - int user = user_mode(regs); int cpu = smp_processor_id(); - /* - * The profiling function is SMP safe. (nothing can mess - * around with "current", and the profiling counters are - * updated with atomic operations). This is especially - * useful with a profiling multiplier != 1 - */ - if (!user) - x86_do_profile(regs->eip); + x86_do_profile(regs); if (--prof_counter[cpu] <= 0) { /* @@ -1036,7 +1028,7 @@ inline void smp_local_timer_interrupt(struct pt_regs * regs) } #ifdef CONFIG_SMP - update_process_times(user); + update_process_times(user_mode(regs)); #endif } diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index 79c204a1f476..9314e0b9f880 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -167,6 +167,9 @@ EXPORT_SYMBOL(get_wchan); EXPORT_SYMBOL(rtc_lock); +EXPORT_SYMBOL_GPL(register_profile_notifier); +EXPORT_SYMBOL_GPL(unregister_profile_notifier); + #undef memcpy #undef memset extern void * memset(void *,int,__kernel_size_t); diff --git a/arch/i386/kernel/profile.c b/arch/i386/kernel/profile.c new file mode 100644 index 000000000000..334af20585cb --- /dev/null +++ b/arch/i386/kernel/profile.c @@ -0,0 +1,45 @@ +/* + * linux/arch/i386/kernel/profile.c + * + * (C) 2002 John Levon + * + */ + +#include +#include +#include +#include +#include + +static struct notifier_block * profile_listeners; +static rwlock_t profile_lock = RW_LOCK_UNLOCKED; + +int register_profile_notifier(struct notifier_block * nb) +{ + int err; + write_lock_irq(&profile_lock); + err = notifier_chain_register(&profile_listeners, nb); + write_unlock_irq(&profile_lock); + return err; +} + + +int unregister_profile_notifier(struct notifier_block * nb) +{ + int err; + write_lock_irq(&profile_lock); + err = notifier_chain_unregister(&profile_listeners, nb); + write_unlock_irq(&profile_lock); + return err; +} + + +void x86_profile_hook(struct pt_regs * regs) +{ + /* we would not even need this lock if + * we had a global cli() on register/unregister + */ + read_lock(&profile_lock); + notifier_call_chain(&profile_listeners, 0, regs); + read_unlock(&profile_lock); +} diff --git a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c index 4e3b4f1cb4b3..cf53d2c1d50a 100644 --- a/arch/i386/kernel/time.c +++ b/arch/i386/kernel/time.c @@ -64,11 +64,6 @@ extern spinlock_t i8259A_lock; #include "do_timer.h" -/* - * for x86_do_profile() - */ -#include - u64 jiffies_64; unsigned long cpu_khz; /* Detected as we calibrate the TSC */ diff --git a/arch/i386/mach-generic/do_timer.h b/arch/i386/mach-generic/do_timer.h index 7ee964b2ebf2..4a24f8ad0635 100644 --- a/arch/i386/mach-generic/do_timer.h +++ b/arch/i386/mach-generic/do_timer.h @@ -20,8 +20,7 @@ static inline void do_timer_interrupt_hook(struct pt_regs *regs) * system, in that case we have to call the local interrupt handler. */ #ifndef CONFIG_X86_LOCAL_APIC - if (!user_mode(regs)) - x86_do_profile(regs->eip); + x86_do_profile(regs); #else if (!using_apic_timer) smp_local_timer_interrupt(regs); diff --git a/arch/i386/mach-visws/do_timer.h b/arch/i386/mach-visws/do_timer.h index b2c1cbed5cb9..d19c7063e17d 100644 --- a/arch/i386/mach-visws/do_timer.h +++ b/arch/i386/mach-visws/do_timer.h @@ -15,8 +15,7 @@ static inline void do_timer_interrupt_hook(struct pt_regs *regs) * system, in that case we have to call the local interrupt handler. */ #ifndef CONFIG_X86_LOCAL_APIC - if (!user_mode(regs)) - x86_do_profile(regs->eip); + x86_do_profile(regs); #else if (!using_apic_timer) smp_local_timer_interrupt(regs); diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index 7bdea5bbe922..cbafa4129498 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include diff --git a/include/asm-i386/hw_irq.h b/include/asm-i386/hw_irq.h index f23f4f75ce65..1a60daa9172e 100644 --- a/include/asm-i386/hw_irq.h +++ b/include/asm-i386/hw_irq.h @@ -13,6 +13,7 @@ */ #include +#include #include #include @@ -65,20 +66,31 @@ extern char _stext, _etext; #define IO_APIC_IRQ(x) (((x) >= 16) || ((1<<(x)) & io_apic_irqs)) -extern unsigned long prof_cpu_mask; -extern unsigned int * prof_buffer; -extern unsigned long prof_len; -extern unsigned long prof_shift; - /* - * x86 profiling function, SMP safe. We might want to do this in - * assembly totally? + * The profiling function is SMP safe. (nothing can mess + * around with "current", and the profiling counters are + * updated with atomic operations). This is especially + * useful with a profiling multiplier != 1 */ -static inline void x86_do_profile (unsigned long eip) +static inline void x86_do_profile(struct pt_regs * regs) { + unsigned long eip; + extern unsigned long prof_cpu_mask; + extern char _stext; +#ifdef CONFIG_PROFILING + extern void x86_profile_hook(struct pt_regs *); + + x86_profile_hook(regs); +#endif + + if (user_mode(regs)) + return; + if (!prof_buffer) return; + eip = regs->eip; + /* * Only measure the CPUs specified by /proc/irq/prof_cpu_mask. * (default is all CPUs.) @@ -97,7 +109,28 @@ static inline void x86_do_profile (unsigned long eip) eip = prof_len-1; atomic_inc((atomic_t *)&prof_buffer[eip]); } + +struct notifier_block; + +#ifdef CONFIG_PROFILING + +int register_profile_notifier(struct notifier_block * nb); +int unregister_profile_notifier(struct notifier_block * nb); + +#else + +static inline int register_profile_notifier(struct notifier_block * nb) +{ + return -ENOSYS; +} + +static inline int unregister_profile_notifier(struct notifier_block * nb) +{ + return -ENOSYS; +} +#endif /* CONFIG_PROFILING */ + #ifdef CONFIG_SMP /*more of this file should probably be ifdefed SMP */ static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) { if (IO_APIC_IRQ(i)) diff --git a/include/linux/profile.h b/include/linux/profile.h index 15c1e91198b0..11fbe9cec572 100644 --- a/include/linux/profile.h +++ b/include/linux/profile.h @@ -8,6 +8,17 @@ #include #include +/* parse command line */ +int __init profile_setup(char * str); + +/* init basic kernel profiler */ +void __init profile_init(void); + +extern unsigned int * prof_buffer; +extern unsigned long prof_len; +extern unsigned long prof_shift; + + enum profile_type { EXIT_TASK, EXIT_MMAP, diff --git a/include/linux/sched.h b/include/linux/sched.h index 89c4ead4cf4b..764a3ebf3c24 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -492,10 +492,6 @@ extern unsigned long itimer_ticks; extern unsigned long itimer_next; extern void do_timer(struct pt_regs *); -extern unsigned int * prof_buffer; -extern unsigned long prof_len; -extern unsigned long prof_shift; - extern void FASTCALL(__wake_up(wait_queue_head_t *q, unsigned int mode, int nr)); extern void FASTCALL(__wake_up_locked(wait_queue_head_t *q, unsigned int mode)); extern void FASTCALL(__wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr)); diff --git a/init/main.c b/init/main.c index c6023edc03f3..1850a1c3686d 100644 --- a/init/main.c +++ b/init/main.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -52,7 +53,6 @@ #error Sorry, your GCC is too old. It builds incorrect kernels. #endif -extern char _stext, _etext; extern char *linux_banner; static int init(void *); @@ -130,13 +130,6 @@ __setup("maxcpus=", maxcpus); static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, }; char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, }; -static int __init profile_setup(char *str) -{ - int par; - if (get_option(&str,&par)) prof_shift = par; - return 1; -} - __setup("profile=", profile_setup); static int __init checksetup(char *line) @@ -411,16 +404,7 @@ asmlinkage void __init start_kernel(void) #ifdef CONFIG_MODULES init_modules(); #endif - if (prof_shift) { - unsigned int size; - /* only text is profiled */ - prof_len = (unsigned long) &_etext - (unsigned long) &_stext; - prof_len >>= prof_shift; - - size = prof_len * sizeof(unsigned int) + PAGE_SIZE-1; - prof_buffer = (unsigned int *) alloc_bootmem(size); - } - + profile_init(); kmem_cache_init(); local_irq_enable(); calibrate_delay(); diff --git a/kernel/profile.c b/kernel/profile.c index 7ebffe971ca8..756f142b1f35 100644 --- a/kernel/profile.c +++ b/kernel/profile.c @@ -9,6 +9,36 @@ #include #include +extern char _stext, _etext; + +unsigned int * prof_buffer; +unsigned long prof_len; +unsigned long prof_shift; + +int __init profile_setup(char * str) +{ + int par; + if (get_option(&str,&par)) + prof_shift = par; + return 1; +} + + +void __init profile_init(void) +{ + unsigned int size; + + if (!prof_shift) + return; + + /* only text is profiled */ + prof_len = (unsigned long) &_etext - (unsigned long) &_stext; + prof_len >>= prof_shift; + + size = prof_len * sizeof(unsigned int) + PAGE_SIZE - 1; + prof_buffer = (unsigned int *) alloc_bootmem(size); +} + /* Profile event notifications */ #ifdef CONFIG_PROFILING diff --git a/kernel/timer.c b/kernel/timer.c index bf0077634c93..2d30f7fd0ecb 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -406,10 +406,6 @@ long time_adj; /* tick adjust (scaled 1 / HZ) */ long time_reftime; /* time at last adjustment (s) */ long time_adjust; -unsigned int * prof_buffer; -unsigned long prof_len; -unsigned long prof_shift; - /* * this routine handles the overflow of the microsecond field * -- cgit v1.2.3 From e0346c53d1cff03283c16b4b1527bb91b223e179 Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 15 Oct 2002 04:30:44 -0700 Subject: [PATCH] oprofile - NMI hook This provides a simple api to let oprofile hook into the NMI interrupt for the perfctr profiler. --- arch/i386/kernel/i386_ksyms.c | 7 +++++++ arch/i386/kernel/nmi.c | 12 +++++++++++ arch/i386/kernel/traps.c | 37 +++++++++++++++++++++++++++----- include/asm-i386/nmi.h | 49 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 include/asm-i386/nmi.h (limited to 'include') diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index 9314e0b9f880..f9dc46f34e6d 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -29,6 +29,7 @@ #include #include #include +#include extern void dump_thread(struct pt_regs *, struct user *); extern spinlock_t rtc_lock; @@ -151,6 +152,10 @@ EXPORT_SYMBOL(smp_call_function); EXPORT_SYMBOL(flush_tlb_page); #endif +#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_PM) +EXPORT_SYMBOL_GPL(set_nmi_pm_callback); +EXPORT_SYMBOL_GPL(unset_nmi_pm_callback); +#endif #ifdef CONFIG_X86_IO_APIC EXPORT_SYMBOL(IO_APIC_get_PCI_irq_vector); #endif @@ -169,6 +174,8 @@ EXPORT_SYMBOL(rtc_lock); EXPORT_SYMBOL_GPL(register_profile_notifier); EXPORT_SYMBOL_GPL(unregister_profile_notifier); +EXPORT_SYMBOL_GPL(set_nmi_callback); +EXPORT_SYMBOL_GPL(unset_nmi_callback); #undef memcpy #undef memset diff --git a/arch/i386/kernel/nmi.c b/arch/i386/kernel/nmi.c index fb2026daf3f1..fbae2c8deeaf 100644 --- a/arch/i386/kernel/nmi.c +++ b/arch/i386/kernel/nmi.c @@ -175,6 +175,18 @@ static int nmi_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) return 0; } +struct pm_dev * set_nmi_pm_callback(pm_callback callback) +{ + apic_pm_unregister(nmi_pmdev); + return apic_pm_register(PM_SYS_DEV, 0, callback); +} + +void unset_nmi_pm_callback(struct pm_dev * dev) +{ + apic_pm_unregister(dev); + nmi_pmdev = apic_pm_register(PM_SYS_DEV, 0, nmi_pm_callback); +} + static void nmi_pm_init(void) { if (!nmi_pmdev) diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 68ed7969fe6d..3eeb2c41814b 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -478,17 +479,16 @@ static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs) return; } #endif - printk("Uhhuh. NMI received for unknown reason %02x.\n", reason); + printk("Uhhuh. NMI received for unknown reason %02x on CPU %d.\n", + reason, smp_processor_id()); printk("Dazed and confused, but trying to continue\n"); printk("Do you have a strange power saving mode enabled?\n"); } -asmlinkage void do_nmi(struct pt_regs * regs, long error_code) +static void default_do_nmi(struct pt_regs * regs) { unsigned char reason = inb(0x61); - - ++nmi_count(smp_processor_id()); - + if (!(reason & 0xc0)) { #if CONFIG_X86_LOCAL_APIC /* @@ -517,6 +517,33 @@ asmlinkage void do_nmi(struct pt_regs * regs, long error_code) inb(0x71); /* dummy */ } +static int dummy_nmi_callback(struct pt_regs * regs, int cpu) +{ + return 0; +} + +static nmi_callback_t nmi_callback = dummy_nmi_callback; + +asmlinkage void do_nmi(struct pt_regs * regs, long error_code) +{ + int cpu = smp_processor_id(); + + ++nmi_count(cpu); + + if (!nmi_callback(regs, cpu)) + default_do_nmi(regs); +} + +void set_nmi_callback(nmi_callback_t callback) +{ + nmi_callback = callback; +} + +void unset_nmi_callback(void) +{ + nmi_callback = dummy_nmi_callback; +} + /* * Our handling of the processor debug registers is non-trivial. * We do not clear them on entry and exit from the kernel. Therefore diff --git a/include/asm-i386/nmi.h b/include/asm-i386/nmi.h new file mode 100644 index 000000000000..d20f0fb9ad2b --- /dev/null +++ b/include/asm-i386/nmi.h @@ -0,0 +1,49 @@ +/* + * linux/include/asm-i386/nmi.h + */ +#ifndef ASM_NMI_H +#define ASM_NMI_H + +#include + +struct pt_regs; + +typedef int (*nmi_callback_t)(struct pt_regs * regs, int cpu); + +/** + * set_nmi_callback + * + * Set a handler for an NMI. Only one handler may be + * set. Return 1 if the NMI was handled. + */ +void set_nmi_callback(nmi_callback_t callback); + +/** + * unset_nmi_callback + * + * Remove the handler previously set. + */ +void unset_nmi_callback(void); + +#ifdef CONFIG_PM + +/** Replace the PM callback routine for NMI. */ +struct pm_dev * set_nmi_pm_callback(pm_callback callback); + +/** Unset the PM callback routine back to the default. */ +void unset_nmi_pm_callback(struct pm_dev * dev); + +#else + +static inline struct pm_dev * set_nmi_pm_callback(pm_callback callback) +{ + return 0; +} + +static inline void unset_nmi_pm_callback(struct pm_dev * dev) +{ +} + +#endif /* CONFIG_PM */ + +#endif /* ASM_NMI_H */ -- cgit v1.2.3 From 7b401a1309642111dec4f42f4a0491e366c2ae38 Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 15 Oct 2002 04:30:51 -0700 Subject: [PATCH] oprofile - MSR defines Add the MSR defines oprofile uses --- include/asm-i386/msr.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/asm-i386/msr.h b/include/asm-i386/msr.h index b1b5ae1ce148..8eefc078d95e 100644 --- a/include/asm-i386/msr.h +++ b/include/asm-i386/msr.h @@ -99,7 +99,13 @@ #define MSR_K6_PFIR 0xC0000088 #define MSR_K7_EVNTSEL0 0xC0010000 +#define MSR_K7_EVNTSEL1 0xC0010001 +#define MSR_K7_EVNTSEL2 0xC0010002 +#define MSR_K7_EVNTSEL3 0xC0010003 #define MSR_K7_PERFCTR0 0xC0010004 +#define MSR_K7_PERFCTR1 0xC0010005 +#define MSR_K7_PERFCTR2 0xC0010006 +#define MSR_K7_PERFCTR3 0xC0010007 #define MSR_K7_HWCR 0xC0010015 #define MSR_K7_FID_VID_CTL 0xC0010041 #define MSR_K7_VID_STATUS 0xC0010042 -- cgit v1.2.3 From 99ee21eefb9e88a850072e7dab6f0f078f560ece Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 15 Oct 2002 04:30:56 -0700 Subject: [PATCH] oprofile - core Add the oprofile core. The core design is very similar to that we discussed in private mail. The nasty details should be documented in the patch below. --- drivers/oprofile/buffer_sync.c | 394 ++++++++++++++++++++++++++++++++++++++ drivers/oprofile/buffer_sync.h | 19 ++ drivers/oprofile/cpu_buffer.c | 135 +++++++++++++ drivers/oprofile/cpu_buffer.h | 45 +++++ drivers/oprofile/event_buffer.c | 186 ++++++++++++++++++ drivers/oprofile/event_buffer.h | 42 ++++ drivers/oprofile/oprof.c | 154 +++++++++++++++ drivers/oprofile/oprof.h | 34 ++++ drivers/oprofile/oprofile_files.c | 91 +++++++++ drivers/oprofile/oprofile_stats.c | 77 ++++++++ drivers/oprofile/oprofile_stats.h | 31 +++ drivers/oprofile/oprofilefs.c | 306 +++++++++++++++++++++++++++++ include/linux/oprofile.h | 98 ++++++++++ 13 files changed, 1612 insertions(+) create mode 100644 drivers/oprofile/buffer_sync.c create mode 100644 drivers/oprofile/buffer_sync.h create mode 100644 drivers/oprofile/cpu_buffer.c create mode 100644 drivers/oprofile/cpu_buffer.h create mode 100644 drivers/oprofile/event_buffer.c create mode 100644 drivers/oprofile/event_buffer.h create mode 100644 drivers/oprofile/oprof.c create mode 100644 drivers/oprofile/oprof.h create mode 100644 drivers/oprofile/oprofile_files.c create mode 100644 drivers/oprofile/oprofile_stats.c create mode 100644 drivers/oprofile/oprofile_stats.h create mode 100644 drivers/oprofile/oprofilefs.c create mode 100644 include/linux/oprofile.h (limited to 'include') diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c new file mode 100644 index 000000000000..46360ee22da2 --- /dev/null +++ b/drivers/oprofile/buffer_sync.c @@ -0,0 +1,394 @@ +/** + * @file buffer_sync.c + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + * + * This is the core of the buffer management. Each + * CPU buffer is processed and entered into the + * global event buffer. Such processing is necessary + * in several circumstances, mentioned below. + * + * The processing does the job of converting the + * transitory EIP value into a persistent dentry/offset + * value that the profiler can record at its leisure. + * + * See fs/dcookies.c for a description of the dentry/offset + * objects. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "event_buffer.h" +#include "cpu_buffer.h" +#include "oprofile_stats.h" + +#define DEFAULT_EXPIRE (HZ / 4) + +static void wq_sync_buffers(void *); +static DECLARE_WORK(sync_wq, wq_sync_buffers, 0); + +static struct timer_list sync_timer; +static void timer_ping(unsigned long data); +static void sync_cpu_buffers(void); + + +/* We must make sure to process every entry in the CPU buffers + * before a task got the PF_EXITING flag, otherwise we will hold + * references to a possibly freed task_struct. We are safe with + * samples past the PF_EXITING point in do_exit(), because we + * explicitly check for that in cpu_buffer.c + */ +static int exit_task_notify(struct notifier_block * self, unsigned long val, void * data) +{ + sync_cpu_buffers(); + return 0; +} + +/* There are two cases of tasks modifying task->mm->mmap list we + * must concern ourselves with. First, when a task is about to + * exit (exit_mmap()), we should process the buffer to deal with + * any samples in the CPU buffer, before we lose the ->mmap information + * we need. Second, a task may unmap (part of) an executable mmap, + * so we want to process samples before that happens too + */ +static int mm_notify(struct notifier_block * self, unsigned long val, void * data) +{ + sync_cpu_buffers(); + return 0; +} + + +static struct notifier_block exit_task_nb = { + .notifier_call = exit_task_notify, +}; + +static struct notifier_block exec_unmap_nb = { + .notifier_call = mm_notify, +}; + +static struct notifier_block exit_mmap_nb = { + .notifier_call = mm_notify, +}; + + +int sync_start(void) +{ + int err = profile_event_register(EXIT_TASK, &exit_task_nb); + if (err) + goto out; + err = profile_event_register(EXIT_MMAP, &exit_mmap_nb); + if (err) + goto out2; + err = profile_event_register(EXEC_UNMAP, &exec_unmap_nb); + if (err) + goto out3; + + sync_timer.function = timer_ping; + sync_timer.expires = jiffies + DEFAULT_EXPIRE; + add_timer(&sync_timer); +out: + return err; +out3: + profile_event_unregister(EXIT_MMAP, &exit_mmap_nb); +out2: + profile_event_unregister(EXIT_TASK, &exit_task_nb); + goto out; +} + + +void sync_stop(void) +{ + profile_event_unregister(EXIT_TASK, &exit_task_nb); + profile_event_unregister(EXIT_MMAP, &exit_mmap_nb); + profile_event_unregister(EXEC_UNMAP, &exec_unmap_nb); + del_timer_sync(&sync_timer); +} + + +/* Optimisation. We can manage without taking the dcookie sem + * because we cannot reach this code without at least one + * dcookie user still being registered (namely, the reader + * of the event buffer). */ +static inline unsigned long fast_get_dcookie(struct dentry * dentry, + struct vfsmount * vfsmnt) +{ + unsigned long cookie; + + if (dentry->d_cookie) + return (unsigned long)dentry; + get_dcookie(dentry, vfsmnt, &cookie); + return cookie; +} + + +/* Look up the dcookie for the task's first VM_EXECUTABLE mapping, + * which corresponds loosely to "application name". This is + * not strictly necessary but allows oprofile to associate + * shared-library samples with particular applications + */ +static unsigned long get_exec_dcookie(struct mm_struct * mm) +{ + unsigned long cookie = 0; + struct vm_area_struct * vma; + + if (!mm) + goto out; + + for (vma = mm->mmap; vma; vma = vma->vm_next) { + if (!vma->vm_file) + continue; + if (!vma->vm_flags & VM_EXECUTABLE) + continue; + cookie = fast_get_dcookie(vma->vm_file->f_dentry, + vma->vm_file->f_vfsmnt); + break; + } + +out: + return cookie; +} + + +/* Convert the EIP value of a sample into a persistent dentry/offset + * pair that can then be added to the global event buffer. We make + * sure to do this lookup before a mm->mmap modification happens so + * we don't lose track. + */ +static unsigned long lookup_dcookie(struct mm_struct * mm, unsigned long addr, off_t * offset) +{ + unsigned long cookie = 0; + struct vm_area_struct * vma; + + for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) { + if (!vma) + goto out; + + if (!vma->vm_file) + continue; + + if (addr < vma->vm_start || addr >= vma->vm_end) + continue; + + cookie = fast_get_dcookie(vma->vm_file->f_dentry, + vma->vm_file->f_vfsmnt); + *offset = (vma->vm_pgoff << PAGE_SHIFT) + addr - vma->vm_start; + break; + } +out: + return cookie; +} + + +static unsigned long last_cookie = ~0UL; + +static void add_cpu_switch(int i) +{ + add_event_entry(ESCAPE_CODE); + add_event_entry(CPU_SWITCH_CODE); + add_event_entry(i); + last_cookie = ~0UL; +} + + +static void add_ctx_switch(pid_t pid, unsigned long cookie) +{ + add_event_entry(ESCAPE_CODE); + add_event_entry(CTX_SWITCH_CODE); + add_event_entry(pid); + add_event_entry(cookie); +} + + +static void add_cookie_switch(unsigned long cookie) +{ + add_event_entry(ESCAPE_CODE); + add_event_entry(COOKIE_SWITCH_CODE); + add_event_entry(cookie); +} + + +static void add_sample_entry(unsigned long offset, unsigned long event) +{ + add_event_entry(offset); + add_event_entry(event); +} + + +static void add_us_sample(struct mm_struct * mm, struct op_sample * s) +{ + unsigned long cookie; + off_t offset; + + cookie = lookup_dcookie(mm, s->eip, &offset); + + if (!cookie) + return; + + if (cookie != last_cookie) { + add_cookie_switch(cookie); + last_cookie = cookie; + } + + add_sample_entry(offset, s->event); +} + + +static inline int is_kernel(unsigned long val) +{ + return val > __PAGE_OFFSET; +} + + +/* Add a sample to the global event buffer. If possible the + * sample is converted into a persistent dentry/offset pair + * for later lookup from userspace. + */ +static void add_sample(struct mm_struct * mm, struct op_sample * s) +{ + if (is_kernel(s->eip)) { + add_sample_entry(s->eip, s->event); + } else if (mm) { + add_us_sample(mm, s); + } +} + + +static void release_mm(struct mm_struct * mm) +{ + if (mm) + up_read(&mm->mmap_sem); +} + + +/* Take the task's mmap_sem to protect ourselves from + * races when we do lookup_dcookie(). + */ +static struct mm_struct * take_task_mm(struct task_struct * task) +{ + struct mm_struct * mm; + task_lock(task); + mm = task->mm; + task_unlock(task); + + /* if task->mm !NULL, mm_count must be at least 1. It cannot + * drop to 0 without the task exiting, which will have to sleep + * on buffer_sem first. So we do not need to mark mm_count + * ourselves. + */ + if (mm) { + /* More ugliness. If a task took its mmap + * sem then came to sleep on buffer_sem we + * will deadlock waiting for it. So we can + * but try. This will lose samples :/ + */ + if (!down_read_trylock(&mm->mmap_sem)) { + /* FIXME: this underestimates samples lost */ + atomic_inc(&oprofile_stats.sample_lost_mmap_sem); + mm = NULL; + } + } + + return mm; +} + + +static inline int is_ctx_switch(unsigned long val) +{ + return val == ~0UL; +} + + +/* Sync one of the CPU's buffers into the global event buffer. + * Here we need to go through each batch of samples punctuated + * by context switch notes, taking the task's mmap_sem and doing + * lookup in task->mm->mmap to convert EIP into dcookie/offset + * value. + */ +static void sync_buffer(struct oprofile_cpu_buffer * cpu_buf) +{ + struct mm_struct * mm = 0; + struct task_struct * new; + unsigned long cookie; + int i; + + for (i=0; i < cpu_buf->pos; ++i) { + struct op_sample * s = &cpu_buf->buffer[i]; + + if (is_ctx_switch(s->eip)) { + new = (struct task_struct *)s->event; + + release_mm(mm); + mm = take_task_mm(new); + + cookie = get_exec_dcookie(mm); + add_ctx_switch(new->pid, cookie); + } else { + add_sample(mm, s); + } + } + release_mm(mm); + + cpu_buf->pos = 0; +} + + +/* Process each CPU's local buffer into the global + * event buffer. + */ +static void sync_cpu_buffers(void) +{ + int i; + + down(&buffer_sem); + + for (i = 0; i < NR_CPUS; ++i) { + struct oprofile_cpu_buffer * cpu_buf; + + if (!cpu_possible(i)) + continue; + + cpu_buf = &cpu_buffer[i]; + + /* We take a spin lock even though we might + * sleep. It's OK because other users are try + * lockers only, and this region is already + * protected by buffer_sem. It's raw to prevent + * the preempt bogometer firing. Fruity, huh ? */ + _raw_spin_lock(&cpu_buf->int_lock); + add_cpu_switch(i); + sync_buffer(cpu_buf); + _raw_spin_unlock(&cpu_buf->int_lock); + } + + up(&buffer_sem); + + mod_timer(&sync_timer, jiffies + DEFAULT_EXPIRE); +} + + +static void wq_sync_buffers(void * data) +{ + sync_cpu_buffers(); +} + + +/* It is possible that we could have no munmap() or + * other events for a period of time. This will lead + * the CPU buffers to overflow and lose samples and + * context switches. We try to reduce the problem + * by timing out when nothing happens for a while. + */ +static void timer_ping(unsigned long data) +{ + schedule_work(&sync_wq); + /* timer is re-added by the scheduled task */ +} diff --git a/drivers/oprofile/buffer_sync.h b/drivers/oprofile/buffer_sync.h new file mode 100644 index 000000000000..a8def27d8502 --- /dev/null +++ b/drivers/oprofile/buffer_sync.h @@ -0,0 +1,19 @@ +/** + * @file buffer_sync.h + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + */ + +#ifndef OPROFILE_BUFFER_SYNC_H +#define OPROFILE_BUFFER_SYNC_H + +/* add the necessary profiling hooks */ +int sync_start(void); + +/* remove the hooks */ +void sync_stop(void); + +#endif /* OPROFILE_BUFFER_SYNC_H */ diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c new file mode 100644 index 000000000000..42af606defd4 --- /dev/null +++ b/drivers/oprofile/cpu_buffer.c @@ -0,0 +1,135 @@ +/** + * @file cpu_buffer.c + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + * + * Each CPU has a local buffer that stores PC value/event + * pairs. We also log context switches when we notice them. + * Eventually each CPU's buffer is processed into the global + * event buffer by sync_cpu_buffers(). + * + * We use a local buffer for two reasons: an NMI or similar + * interrupt cannot synchronise, and high sampling rates + * would lead to catastrophic global synchronisation if + * a global buffer was used. + */ + +#include +#include +#include + +#include "cpu_buffer.h" +#include "oprof.h" +#include "oprofile_stats.h" + +struct oprofile_cpu_buffer cpu_buffer[NR_CPUS] __cacheline_aligned; + +static unsigned long buffer_size; + +static void __free_cpu_buffers(int num) +{ + int i; + + for (i=0; i < num; ++i) { + struct oprofile_cpu_buffer * b = &cpu_buffer[i]; + + if (!cpu_possible(i)) + continue; + + vfree(b->buffer); + } +} + + +int alloc_cpu_buffers(void) +{ + int i; + + buffer_size = fs_cpu_buffer_size; + + for (i=0; i < NR_CPUS; ++i) { + struct oprofile_cpu_buffer * b = &cpu_buffer[i]; + + if (!cpu_possible(i)) + continue; + + b->buffer = vmalloc(sizeof(struct op_sample) * buffer_size); + if (!b->buffer) + goto fail; + + spin_lock_init(&b->int_lock); + b->pos = 0; + b->last_task = 0; + b->sample_received = 0; + b->sample_lost_locked = 0; + b->sample_lost_overflow = 0; + } + return 0; +fail: + __free_cpu_buffers(i); + return -ENOMEM; +} + + +void free_cpu_buffers(void) +{ + __free_cpu_buffers(NR_CPUS); +} + + +/* Note we can't use a semaphore here as this is supposed to + * be safe from any context. Instead we trylock the CPU's int_lock. + * int_lock is taken by the processing code in sync_cpu_buffers() + * so we avoid disturbing that. + */ +void oprofile_add_sample(unsigned long eip, unsigned long event, int cpu) +{ + struct oprofile_cpu_buffer * cpu_buf = &cpu_buffer[cpu]; + struct task_struct * task; + + /* temporary ? */ + BUG_ON(!oprofile_started); + + cpu_buf->sample_received++; + + if (!spin_trylock(&cpu_buf->int_lock)) { + cpu_buf->sample_lost_locked++; + return; + } + + if (cpu_buf->pos > buffer_size - 2) { + cpu_buf->sample_lost_overflow++; + goto out; + } + + task = current; + + /* notice a task switch */ + if (cpu_buf->last_task != task) { + cpu_buf->last_task = task; + if (!(task->flags & PF_EXITING)) { + cpu_buf->buffer[cpu_buf->pos].eip = ~0UL; + cpu_buf->buffer[cpu_buf->pos].event = (unsigned long)task; + cpu_buf->pos++; + } + } + + /* If the task is exiting it's not safe to take a sample + * as the task_struct is about to be freed. We can't just + * notify at release_task() time because of CLONE_DETACHED + * tasks that release_task() themselves. + */ + if (task->flags & PF_EXITING) { + cpu_buf->sample_lost_task_exit++; + goto out; + } + + cpu_buf->buffer[cpu_buf->pos].eip = eip; + cpu_buf->buffer[cpu_buf->pos].event = event; + cpu_buf->pos++; +out: + spin_unlock(&cpu_buf->int_lock); +} diff --git a/drivers/oprofile/cpu_buffer.h b/drivers/oprofile/cpu_buffer.h new file mode 100644 index 000000000000..87ce0a18550d --- /dev/null +++ b/drivers/oprofile/cpu_buffer.h @@ -0,0 +1,45 @@ +/** + * @file cpu_buffer.h + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + */ + +#ifndef OPROFILE_CPU_BUFFER_H +#define OPROFILE_CPU_BUFFER_H + +#include +#include + +struct task_struct; + +/* allocate a sample buffer for each CPU */ +int alloc_cpu_buffers(void); + +void free_cpu_buffers(void); + +/* CPU buffer is composed of such entries (which are + * also used for context switch notes) + */ +struct op_sample { + unsigned long eip; + unsigned long event; +}; + +struct oprofile_cpu_buffer { + spinlock_t int_lock; + /* protected by int_lock */ + unsigned long pos; + struct task_struct * last_task; + struct op_sample * buffer; + unsigned long sample_received; + unsigned long sample_lost_locked; + unsigned long sample_lost_overflow; + unsigned long sample_lost_task_exit; +} ____cacheline_aligned; + +extern struct oprofile_cpu_buffer cpu_buffer[]; + +#endif /* OPROFILE_CPU_BUFFER_H */ diff --git a/drivers/oprofile/event_buffer.c b/drivers/oprofile/event_buffer.c new file mode 100644 index 000000000000..3552be34eca7 --- /dev/null +++ b/drivers/oprofile/event_buffer.c @@ -0,0 +1,186 @@ +/** + * @file event_buffer.c + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + * + * This is the global event buffer that the user-space + * daemon reads from. The event buffer is an untyped array + * of unsigned longs. Entries are prefixed by the + * escape value ESCAPE_CODE followed by an identifying code. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "event_buffer.h" +#include "cpu_buffer.h" +#include "oprof.h" +#include "oprofile_stats.h" + +DECLARE_MUTEX(buffer_sem); + +static unsigned long buffer_opened; +static DECLARE_WAIT_QUEUE_HEAD(buffer_wait); +static unsigned long * event_buffer; +static unsigned long buffer_size; +static unsigned long buffer_watershed; +static size_t buffer_pos; +/* atomic_t because wait_event checks it outside of buffer_sem */ +static atomic_t buffer_ready = ATOMIC_INIT(0); + +/* Add an entry to the event buffer. When we + * get near to the end we wake up the process + * sleeping on the read() of the file. + */ +void add_event_entry(unsigned long value) +{ + if (buffer_pos == buffer_size) { + atomic_inc(&oprofile_stats.event_lost_overflow); + return; + } + + event_buffer[buffer_pos] = value; + if (++buffer_pos == buffer_size - buffer_watershed) { + atomic_set(&buffer_ready, 1); + wake_up(&buffer_wait); + } +} + + +/* Wake up the waiting process if any. This happens + * on "echo 0 >/dev/oprofile/enable" so the daemon + * processes the data remaining in the event buffer. + */ +void wake_up_buffer_waiter(void) +{ + down(&buffer_sem); + atomic_set(&buffer_ready, 1); + wake_up(&buffer_wait); + up(&buffer_sem); +} + + +int alloc_event_buffer(void) +{ + int err = -ENOMEM; + + spin_lock(&oprofilefs_lock); + buffer_size = fs_buffer_size; + buffer_watershed = fs_buffer_watershed; + spin_unlock(&oprofilefs_lock); + + if (buffer_watershed >= buffer_size) + return -EINVAL; + + event_buffer = vmalloc(sizeof(unsigned long) * buffer_size); + if (!event_buffer) + goto out; + + err = 0; +out: + return err; +} + + +void free_event_buffer(void) +{ + vfree(event_buffer); +} + + +int event_buffer_open(struct inode * inode, struct file * file) +{ + int err = -EPERM; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (test_and_set_bit(0, &buffer_opened)) + return -EBUSY; + + /* Register as a user of dcookies + * to ensure they persist for the lifetime of + * the open event file + */ + err = -EINVAL; + file->private_data = dcookie_register(); + if (!file->private_data) + goto out; + + if ((err = oprofile_setup())) + goto fail; + + /* NB: the actual start happens from userspace + * echo 1 >/dev/oprofile/enable + */ + + return 0; + +fail: + dcookie_unregister(file->private_data); +out: + clear_bit(0, &buffer_opened); + return err; +} + + +int event_buffer_release(struct inode * inode, struct file * file) +{ + oprofile_stop(); + oprofile_shutdown(); + dcookie_unregister(file->private_data); + buffer_pos = 0; + atomic_set(&buffer_ready, 0); + clear_bit(0, &buffer_opened); + return 0; +} + + +ssize_t event_buffer_read(struct file * file, char * buf, size_t count, loff_t * offset) +{ + int retval = -EINVAL; + size_t const max = buffer_size * sizeof(unsigned long); + + /* handling partial reads is more trouble than it's worth */ + if (count != max || *offset) + return -EINVAL; + + /* wait for the event buffer to fill up with some data */ + wait_event_interruptible(buffer_wait, atomic_read(&buffer_ready)); + if (signal_pending(current)) + return -EINTR; + + down(&buffer_sem); + + atomic_set(&buffer_ready, 0); + + retval = -EFAULT; + + count = buffer_pos * sizeof(unsigned long); + + if (copy_to_user(buf, event_buffer, count)) + goto out; + + retval = count; + buffer_pos = 0; + +out: + up(&buffer_sem); + return retval; +} + +struct file_operations event_buffer_fops = { + .open = event_buffer_open, + .release = event_buffer_release, + .read = event_buffer_read, +}; diff --git a/drivers/oprofile/event_buffer.h b/drivers/oprofile/event_buffer.h new file mode 100644 index 000000000000..11d2ed4dea42 --- /dev/null +++ b/drivers/oprofile/event_buffer.h @@ -0,0 +1,42 @@ +/** + * @file event_buffer.h + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + */ + +#ifndef EVENT_BUFFER_H +#define EVENT_BUFFER_H + +#include +#include + +int alloc_event_buffer(void); + +void free_event_buffer(void); + +/* wake up the process sleeping on the event file */ +void wake_up_buffer_waiter(void); + +/* Each escaped entry is prefixed by ESCAPE_CODE + * then one of the following codes, then the + * relevant data. + */ +#define ESCAPE_CODE ~0UL +#define CTX_SWITCH_CODE 1 +#define CPU_SWITCH_CODE 2 +#define COOKIE_SWITCH_CODE 3 + +/* add data to the event buffer */ +void add_event_entry(unsigned long data); + +extern struct file_operations event_buffer_fops; + +/* mutex between sync_cpu_buffers() and the + * file reading code. + */ +extern struct semaphore buffer_sem; + +#endif /* EVENT_BUFFER_H */ diff --git a/drivers/oprofile/oprof.c b/drivers/oprofile/oprof.c new file mode 100644 index 000000000000..1cae1bc13c8d --- /dev/null +++ b/drivers/oprofile/oprof.c @@ -0,0 +1,154 @@ +/** + * @file oprof.c + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "oprof.h" +#include "event_buffer.h" +#include "cpu_buffer.h" +#include "buffer_sync.h" +#include "oprofile_stats.h" + +struct oprofile_operations * oprofile_ops; +enum oprofile_cpu oprofile_cpu_type; +unsigned long oprofile_started; +static unsigned long is_setup; +static DECLARE_MUTEX(start_sem); + +int oprofile_setup(void) +{ + int err; + + if ((err = alloc_cpu_buffers())) + goto out; + + if ((err = alloc_event_buffer())) + goto out1; + + if (oprofile_ops->setup && (err = oprofile_ops->setup())) + goto out2; + + /* Note even though this starts part of the + * profiling overhead, it's necessary to prevent + * us missing task deaths and eventually oopsing + * when trying to process the event buffer. + */ + if ((err = sync_start())) + goto out3; + + down(&start_sem); + is_setup = 1; + up(&start_sem); + return 0; + +out3: + if (oprofile_ops->shutdown) + oprofile_ops->shutdown(); +out2: + free_event_buffer(); +out1: + free_cpu_buffers(); +out: + return err; +} + + +/* Actually start profiling (echo 1>/dev/oprofile/enable) */ +int oprofile_start(void) +{ + int err = -EINVAL; + + down(&start_sem); + + if (!is_setup) + goto out; + + err = 0; + + if (oprofile_started) + goto out; + + if ((err = oprofile_ops->start())) + goto out; + + oprofile_started = 1; + oprofile_reset_stats(); +out: + up(&start_sem); + return err; +} + + +/* echo 0>/dev/oprofile/enable */ +void oprofile_stop(void) +{ + down(&start_sem); + if (!oprofile_started) + goto out; + oprofile_ops->stop(); + oprofile_started = 0; + /* wake up the daemon to read what remains */ + wake_up_buffer_waiter(); +out: + up(&start_sem); +} + + +void oprofile_shutdown(void) +{ + sync_stop(); + if (oprofile_ops->shutdown) + oprofile_ops->shutdown(); + /* down() is also necessary to synchronise all pending events + * before freeing */ + down(&buffer_sem); + is_setup = 0; + up(&buffer_sem); + free_event_buffer(); + free_cpu_buffers(); +} + + +static int __init oprofile_init(void) +{ + int err; + + /* Architecture must fill in the interrupt ops and the + * logical CPU type. + */ + err = oprofile_arch_init(&oprofile_ops, &oprofile_cpu_type); + if (err) + goto out; + + err = oprofilefs_register(); + if (err) + goto out; + +out: + return err; +} + + +static void __exit oprofile_exit(void) +{ + oprofilefs_unregister(); +} + +MODULE_LICENSE("GPL"); +module_init(oprofile_init); +module_exit(oprofile_exit); diff --git a/drivers/oprofile/oprof.h b/drivers/oprofile/oprof.h new file mode 100644 index 000000000000..9f19ba5f39b9 --- /dev/null +++ b/drivers/oprofile/oprof.h @@ -0,0 +1,34 @@ +/** + * @file oprof.h + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + */ + +#ifndef OPROF_H +#define OPROF_H + +#include +#include + +int oprofile_setup(void); +void oprofile_shutdown(void); + +int oprofilefs_register(void); +void oprofilefs_unregister(void); + +int oprofile_start(void); +void oprofile_stop(void); + +extern unsigned long fs_buffer_size; +extern unsigned long fs_cpu_buffer_size; +extern unsigned long fs_buffer_watershed; +extern enum oprofile_cpu oprofile_cpu_type; +extern struct oprofile_operations * oprofile_ops; +extern unsigned long oprofile_started; + +void oprofile_create_files(struct super_block * sb, struct dentry * root); + +#endif /* OPROF_H */ diff --git a/drivers/oprofile/oprofile_files.c b/drivers/oprofile/oprofile_files.c new file mode 100644 index 000000000000..22d8bf5994b6 --- /dev/null +++ b/drivers/oprofile/oprofile_files.c @@ -0,0 +1,91 @@ +/** + * @file oprofile_files.c + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + */ + +#include +#include +#include +#include + +#include "oprof.h" +#include "event_buffer.h" +#include "oprofile_stats.h" + +unsigned long fs_buffer_size = 131072; +unsigned long fs_cpu_buffer_size = 8192; +unsigned long fs_buffer_watershed = 32768; /* FIXME: tune */ + + +static int simple_open(struct inode * inode, struct file * filp) +{ + return 0; +} + + +static ssize_t cpu_type_read(struct file * file, char * buf, size_t count, loff_t * offset) +{ + unsigned long cpu_type = oprofile_cpu_type; + + return oprofilefs_ulong_to_user(&cpu_type, buf, count, offset); +} + + +static struct file_operations cpu_type_fops = { + .open = simple_open, + .read = cpu_type_read, +}; + + +static ssize_t enable_read(struct file * file, char * buf, size_t count, loff_t * offset) +{ + return oprofilefs_ulong_to_user(&oprofile_started, buf, count, offset); +} + + +static ssize_t enable_write(struct file *file, char const * buf, size_t count, loff_t * offset) +{ + unsigned long val; + int retval; + + if (*offset) + return -EINVAL; + + retval = oprofilefs_ulong_from_user(&val, buf, count); + if (retval) + return retval; + + if (val) + retval = oprofile_start(); + else + oprofile_stop(); + + if (retval) + return retval; + return count; +} + + +static struct file_operations enable_fops = { + .open = simple_open, + .read = enable_read, + .write = enable_write, +}; + + +void oprofile_create_files(struct super_block * sb, struct dentry * root) +{ + oprofilefs_create_file(sb, root, "enable", &enable_fops); + oprofilefs_create_file(sb, root, "buffer", &event_buffer_fops); + oprofilefs_create_ulong(sb, root, "buffer_size", &fs_buffer_size); + oprofilefs_create_ulong(sb, root, "buffer_watershed", &fs_buffer_watershed); + oprofilefs_create_ulong(sb, root, "cpu_buffer_size", &fs_cpu_buffer_size); + oprofilefs_create_file(sb, root, "cpu_type", &cpu_type_fops); + oprofile_create_stats_files(sb, root); + if (oprofile_ops->create_files) + oprofile_ops->create_files(sb, root); +} diff --git a/drivers/oprofile/oprofile_stats.c b/drivers/oprofile/oprofile_stats.c new file mode 100644 index 000000000000..479d8315558f --- /dev/null +++ b/drivers/oprofile/oprofile_stats.c @@ -0,0 +1,77 @@ +/** + * @file oprofile_stats.c + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + */ + +#include +#include + +#include "oprofile_stats.h" +#include "cpu_buffer.h" + +struct oprofile_stat_struct oprofile_stats; + +void oprofile_reset_stats(void) +{ + struct oprofile_cpu_buffer * cpu_buf; + int i; + + for (i = 0; i < NR_CPUS; ++i) { + if (!cpu_possible(i)) + continue; + + cpu_buf = &cpu_buffer[i]; + cpu_buf->sample_received = 0; + cpu_buf->sample_lost_locked = 0; + cpu_buf->sample_lost_overflow = 0; + cpu_buf->sample_lost_task_exit = 0; + } + + atomic_set(&oprofile_stats.sample_lost_mmap_sem, 0); + atomic_set(&oprofile_stats.event_lost_overflow, 0); +} + + +void oprofile_create_stats_files(struct super_block * sb, struct dentry * root) +{ + struct oprofile_cpu_buffer * cpu_buf; + struct dentry * cpudir; + struct dentry * dir; + char buf[10]; + int i; + + dir = oprofilefs_mkdir(sb, root, "stats"); + if (!dir) + return; + + for (i = 0; i < NR_CPUS; ++i) { + if (!cpu_possible(i)) + continue; + + cpu_buf = &cpu_buffer[i]; + snprintf(buf, 6, "cpu%d", i); + cpudir = oprofilefs_mkdir(sb, dir, buf); + + /* Strictly speaking access to these ulongs is racy, + * but we can't simply lock them, and they are + * informational only. + */ + oprofilefs_create_ro_ulong(sb, cpudir, "sample_received", + &cpu_buf->sample_received); + oprofilefs_create_ro_ulong(sb, cpudir, "sample_lost_locked", + &cpu_buf->sample_lost_locked); + oprofilefs_create_ro_ulong(sb, cpudir, "sample_lost_overflow", + &cpu_buf->sample_lost_overflow); + oprofilefs_create_ro_ulong(sb, cpudir, "sample_lost_task_exit", + &cpu_buf->sample_lost_task_exit); + } + + oprofilefs_create_ro_atomic(sb, dir, "sample_lost_mmap_sem", + &oprofile_stats.sample_lost_mmap_sem); + oprofilefs_create_ro_atomic(sb, dir, "event_lost_overflow", + &oprofile_stats.event_lost_overflow); +} diff --git a/drivers/oprofile/oprofile_stats.h b/drivers/oprofile/oprofile_stats.h new file mode 100644 index 000000000000..8ca3596c2bef --- /dev/null +++ b/drivers/oprofile/oprofile_stats.h @@ -0,0 +1,31 @@ +/** + * @file oprofile_stats.h + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + */ + +#ifndef OPROFILE_STATS_H +#define OPROFILE_STATS_H + +#include + +struct oprofile_stat_struct { + atomic_t sample_lost_mmap_sem; + atomic_t event_lost_overflow; +}; + +extern struct oprofile_stat_struct oprofile_stats; + +/* reset all stats to zero */ +void oprofile_reset_stats(void); + +struct super_block; +struct dentry; + +/* create the stats/ dir */ +void oprofile_create_stats_files(struct super_block * sb, struct dentry * root); + +#endif /* OPROFILE_STATS_H */ diff --git a/drivers/oprofile/oprofilefs.c b/drivers/oprofile/oprofilefs.c new file mode 100644 index 000000000000..a86100975cb8 --- /dev/null +++ b/drivers/oprofile/oprofilefs.c @@ -0,0 +1,306 @@ +/** + * @file oprofilefs.c + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + * + * A simple filesystem for configuration and + * access of oprofile. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "oprof.h" + +#define OPROFILEFS_MAGIC 0x6f70726f + +spinlock_t oprofilefs_lock = SPIN_LOCK_UNLOCKED; + +static struct inode * oprofilefs_get_inode(struct super_block * sb, int mode) +{ + struct inode * inode = new_inode(sb); + + if (inode) { + inode->i_mode = mode; + inode->i_uid = 0; + inode->i_gid = 0; + inode->i_blksize = PAGE_CACHE_SIZE; + inode->i_blocks = 0; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + } + return inode; +} + + +static struct super_operations s_ops = { + .statfs = simple_statfs, + .drop_inode = generic_delete_inode, +}; + +#define TMPBUFSIZE 50 + +ssize_t oprofilefs_ulong_to_user(unsigned long * val, char * buf, size_t count, loff_t * offset) +{ + char tmpbuf[TMPBUFSIZE]; + size_t maxlen; + + if (!count) + return 0; + + spin_lock(&oprofilefs_lock); + maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%lu\n", *val); + spin_unlock(&oprofilefs_lock); + if (maxlen > TMPBUFSIZE) + maxlen = TMPBUFSIZE; + + if (*offset > maxlen) + return 0; + + if (count > maxlen - *offset) + count = maxlen - *offset; + + if (copy_to_user(buf, tmpbuf + *offset, count)) + return -EFAULT; + + *offset += count; + + return count; +} + + +int oprofilefs_ulong_from_user(unsigned long * val, char const * buf, size_t count) +{ + char tmpbuf[TMPBUFSIZE]; + + if (!count) + return 0; + + if (count > TMPBUFSIZE - 1) + return -EINVAL; + + memset(tmpbuf, 0x0, TMPBUFSIZE); + + if (copy_from_user(tmpbuf, buf, count)) + return -EFAULT; + + spin_lock(&oprofilefs_lock); + *val = simple_strtoul(tmpbuf, NULL, 10); + spin_unlock(&oprofilefs_lock); + return 0; +} + + +static ssize_t ulong_read_file(struct file * file, char * buf, size_t count, loff_t * offset) +{ + return oprofilefs_ulong_to_user(file->private_data, buf, count, offset); +} + + +static ssize_t ulong_write_file(struct file * file, char const * buf, size_t count, loff_t * offset) +{ + unsigned long * value = file->private_data; + int retval; + + if (*offset) + return -EINVAL; + + retval = oprofilefs_ulong_from_user(value, buf, count); + + if (retval) + return retval; + return count; +} + + +static int default_open(struct inode * inode, struct file * filp) +{ + if (inode->u.generic_ip) + filp->private_data = inode->u.generic_ip; + return 0; +} + + +static struct file_operations ulong_fops = { + .read = ulong_read_file, + .write = ulong_write_file, + .open = default_open, +}; + + +static struct file_operations ulong_ro_fops = { + .read = ulong_read_file, + .open = default_open, +}; + + +static struct dentry * __oprofilefs_create_file(struct super_block * sb, + struct dentry * root, char const * name, struct file_operations * fops) +{ + struct dentry * dentry; + struct inode * inode; + struct qstr qname; + qname.name = name; + qname.len = strlen(name); + qname.hash = full_name_hash(qname.name, qname.len); + dentry = d_alloc(root, &qname); + if (!dentry) + return 0; + inode = oprofilefs_get_inode(sb, S_IFREG | 0644); + if (!inode) { + dput(dentry); + return 0; + } + inode->i_fop = fops; + d_add(dentry, inode); + return dentry; +} + + +int oprofilefs_create_ulong(struct super_block * sb, struct dentry * root, + char const * name, unsigned long * val) +{ + struct dentry * d = __oprofilefs_create_file(sb, root, name, &ulong_fops); + if (!d) + return -EFAULT; + + d->d_inode->u.generic_ip = val; + return 0; +} + + +int oprofilefs_create_ro_ulong(struct super_block * sb, struct dentry * root, + char const * name, unsigned long * val) +{ + struct dentry * d = __oprofilefs_create_file(sb, root, name, &ulong_ro_fops); + if (!d) + return -EFAULT; + + d->d_inode->u.generic_ip = val; + return 0; +} + + +static ssize_t atomic_read_file(struct file * file, char * buf, size_t count, loff_t * offset) +{ + atomic_t * aval = file->private_data; + unsigned long val = atomic_read(aval); + return oprofilefs_ulong_to_user(&val, buf, count, offset); +} + + +static struct file_operations atomic_ro_fops = { + .read = atomic_read_file, + .open = default_open, +}; + + +int oprofilefs_create_ro_atomic(struct super_block * sb, struct dentry * root, + char const * name, atomic_t * val) +{ + struct dentry * d = __oprofilefs_create_file(sb, root, name, &atomic_ro_fops); + if (!d) + return -EFAULT; + + d->d_inode->u.generic_ip = val; + return 0; +} + + +int oprofilefs_create_file(struct super_block * sb, struct dentry * root, + char const * name, struct file_operations * fops) +{ + if (!__oprofilefs_create_file(sb, root, name, fops)) + return -EFAULT; + return 0; +} + + +struct dentry * oprofilefs_mkdir(struct super_block * sb, + struct dentry * root, char const * name) +{ + struct dentry * dentry; + struct inode * inode; + struct qstr qname; + qname.name = name; + qname.len = strlen(name); + qname.hash = full_name_hash(qname.name, qname.len); + dentry = d_alloc(root, &qname); + if (!dentry) + return 0; + inode = oprofilefs_get_inode(sb, S_IFDIR | 0755); + if (!inode) { + dput(dentry); + return 0; + } + inode->i_op = &simple_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + d_add(dentry, inode); + return dentry; +} + + +static int oprofilefs_fill_super(struct super_block * sb, void * data, int silent) +{ + struct inode * root_inode; + struct dentry * root_dentry; + + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = OPROFILEFS_MAGIC; + sb->s_op = &s_ops; + + root_inode = oprofilefs_get_inode(sb, S_IFDIR | 0755); + if (!root_inode) + return -ENOMEM; + root_inode->i_op = &simple_dir_inode_operations; + root_inode->i_fop = &simple_dir_operations; + root_dentry = d_alloc_root(root_inode); + if (!root_dentry) { + iput(root_inode); + return -ENOMEM; + } + + sb->s_root = root_dentry; + + oprofile_create_files(sb, root_dentry); + + // FIXME: verify kill_litter_super removes our dentries + return 0; +} + + +static struct super_block * oprofilefs_get_sb(struct file_system_type * fs_type, + int flags, char * dev_name, void * data) +{ + return get_sb_single(fs_type, flags, data, oprofilefs_fill_super); +} + + +static struct file_system_type oprofilefs_type = { + .owner = THIS_MODULE, + .name = "oprofilefs", + .get_sb = oprofilefs_get_sb, + .kill_sb = kill_litter_super, +}; + + +int __init oprofilefs_register(void) +{ + return register_filesystem(&oprofilefs_type); +} + + +void __exit oprofilefs_unregister(void) +{ + unregister_filesystem(&oprofilefs_type); +} diff --git a/include/linux/oprofile.h b/include/linux/oprofile.h new file mode 100644 index 000000000000..982b64e0518a --- /dev/null +++ b/include/linux/oprofile.h @@ -0,0 +1,98 @@ +/** + * @file oprofile.h + * + * API for machine-specific interrupts to interface + * to oprofile. + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + */ + +#ifndef OPROFILE_H +#define OPROFILE_H + +#include +#include +#include + +struct super_block; +struct dentry; +struct file_operations; + +enum oprofile_cpu { + OPROFILE_CPU_PPRO, + OPROFILE_CPU_PII, + OPROFILE_CPU_PIII, + OPROFILE_CPU_ATHLON, + OPROFILE_CPU_TIMER +}; + +/* Operations structure to be filled in */ +struct oprofile_operations { + /* create any necessary configuration files in the oprofile fs. + * Optional. */ + int (*create_files)(struct super_block * sb, struct dentry * root); + /* Do any necessary interrupt setup. Optional. */ + int (*setup)(void); + /* Do any necessary interrupt shutdown. Optional. */ + void (*shutdown)(void); + /* Start delivering interrupts. */ + int (*start)(void); + /* Stop delivering interrupts. */ + void (*stop)(void); +}; + +/** + * One-time initialisation. *ops must be set to a filled-in + * operations structure. oprofile_cpu_type must be set. + * Return 0 on success. + */ +int oprofile_arch_init(struct oprofile_operations ** ops, enum oprofile_cpu * cpu); + +/** + * Add a sample. This may be called from any context. Pass + * smp_processor_id() as cpu. + */ +extern void FASTCALL(oprofile_add_sample(unsigned long eip, unsigned long event, int cpu)); + +/** + * Create a file of the given name as a child of the given root, with + * the specified file operations. + */ +int oprofilefs_create_file(struct super_block * sb, struct dentry * root, + char const * name, struct file_operations * fops); + +/** Create a file for read/write access to an unsigned long. */ +int oprofilefs_create_ulong(struct super_block * sb, struct dentry * root, + char const * name, ulong * val); + +/** Create a file for read-only access to an unsigned long. */ +int oprofilefs_create_ro_ulong(struct super_block * sb, struct dentry * root, + char const * name, ulong * val); + +/** Create a file for read-only access to an atomic_t. */ +int oprofilefs_create_ro_atomic(struct super_block * sb, struct dentry * root, + char const * name, atomic_t * val); + +/** create a directory */ +struct dentry * oprofilefs_mkdir(struct super_block * sb, struct dentry * root, + char const * name); + +/** + * Convert an unsigned long value into ASCII and copy it to the user buffer @buf, + * updating *offset appropriately. Returns bytes written or -EFAULT. + */ +ssize_t oprofilefs_ulong_to_user(unsigned long * val, char * buf, size_t count, loff_t * offset); + +/** + * Read an ASCII string for a number from a userspace buffer and fill *val on success. + * Returns 0 on success, < 0 on error. + */ +int oprofilefs_ulong_from_user(unsigned long * val, char const * buf, size_t count); + +/** lock for read/write safety */ +extern spinlock_t oprofilefs_lock; + +#endif /* OPROFILE_H */ -- cgit v1.2.3 From f35e65513f6bd0a346c8e51e78c8893bb3143c9f Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 15 Oct 2002 04:31:08 -0700 Subject: [PATCH] oprofile - dcookies need to use u32 Make dcookies use a stable size regardless of whether we're on a 32-bit or 64-bit platform. --- drivers/oprofile/buffer_sync.c | 24 ++++++++++++------------ drivers/oprofile/oprof.c | 1 - fs/dcookies.c | 14 +++++++------- include/linux/dcookies.h | 4 ++-- 4 files changed, 21 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c index 46360ee22da2..79b92c1c7965 100644 --- a/drivers/oprofile/buffer_sync.c +++ b/drivers/oprofile/buffer_sync.c @@ -118,13 +118,13 @@ void sync_stop(void) * because we cannot reach this code without at least one * dcookie user still being registered (namely, the reader * of the event buffer). */ -static inline unsigned long fast_get_dcookie(struct dentry * dentry, +static inline u32 fast_get_dcookie(struct dentry * dentry, struct vfsmount * vfsmnt) { - unsigned long cookie; + u32 cookie; if (dentry->d_cookie) - return (unsigned long)dentry; + return (u32)dentry; get_dcookie(dentry, vfsmnt, &cookie); return cookie; } @@ -135,9 +135,9 @@ static inline unsigned long fast_get_dcookie(struct dentry * dentry, * not strictly necessary but allows oprofile to associate * shared-library samples with particular applications */ -static unsigned long get_exec_dcookie(struct mm_struct * mm) +static u32 get_exec_dcookie(struct mm_struct * mm) { - unsigned long cookie = 0; + u32 cookie = 0; struct vm_area_struct * vma; if (!mm) @@ -163,9 +163,9 @@ out: * sure to do this lookup before a mm->mmap modification happens so * we don't lose track. */ -static unsigned long lookup_dcookie(struct mm_struct * mm, unsigned long addr, off_t * offset) +static u32 lookup_dcookie(struct mm_struct * mm, unsigned long addr, off_t * offset) { - unsigned long cookie = 0; + u32 cookie = 0; struct vm_area_struct * vma; for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) { @@ -188,7 +188,7 @@ out: } -static unsigned long last_cookie = ~0UL; +static u32 last_cookie = ~0UL; static void add_cpu_switch(int i) { @@ -199,7 +199,7 @@ static void add_cpu_switch(int i) } -static void add_ctx_switch(pid_t pid, unsigned long cookie) +static void add_ctx_switch(pid_t pid, u32 cookie) { add_event_entry(ESCAPE_CODE); add_event_entry(CTX_SWITCH_CODE); @@ -208,7 +208,7 @@ static void add_ctx_switch(pid_t pid, unsigned long cookie) } -static void add_cookie_switch(unsigned long cookie) +static void add_cookie_switch(u32 cookie) { add_event_entry(ESCAPE_CODE); add_event_entry(COOKIE_SWITCH_CODE); @@ -225,7 +225,7 @@ static void add_sample_entry(unsigned long offset, unsigned long event) static void add_us_sample(struct mm_struct * mm, struct op_sample * s) { - unsigned long cookie; + u32 cookie; off_t offset; cookie = lookup_dcookie(mm, s->eip, &offset); @@ -317,7 +317,7 @@ static void sync_buffer(struct oprofile_cpu_buffer * cpu_buf) { struct mm_struct * mm = 0; struct task_struct * new; - unsigned long cookie; + u32 cookie; int i; for (i=0; i < cpu_buf->pos; ++i) { diff --git a/drivers/oprofile/oprof.c b/drivers/oprofile/oprof.c index 1cae1bc13c8d..91e120f1ac75 100644 --- a/drivers/oprofile/oprof.c +++ b/drivers/oprofile/oprof.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/dcookies.c b/fs/dcookies.c index 0236c146b451..d589103eb820 100644 --- a/fs/dcookies.c +++ b/fs/dcookies.c @@ -8,7 +8,7 @@ * non-transitory that can be processed at a later date. * This is done by locking the dentry/vfsmnt pair in the * kernel until released by the tasks needing the persistent - * objects. The tag is simply an unsigned long that refers + * objects. The tag is simply an u32 that refers * to the pair and can be looked up from userspace. */ @@ -46,19 +46,19 @@ static inline int is_live(void) /* The dentry is locked, its address will do for the cookie */ -static inline unsigned long dcookie_value(struct dcookie_struct * dcs) +static inline u32 dcookie_value(struct dcookie_struct * dcs) { - return (unsigned long)dcs->dentry; + return (u32)dcs->dentry; } -static size_t dcookie_hash(unsigned long dcookie) +static size_t dcookie_hash(u32 dcookie) { return (dcookie >> 2) & (hash_size - 1); } -static struct dcookie_struct * find_dcookie(unsigned long dcookie) +static struct dcookie_struct * find_dcookie(u32 dcookie) { struct dcookie_struct * found = 0; struct dcookie_struct * dcs; @@ -109,7 +109,7 @@ static struct dcookie_struct * alloc_dcookie(struct dentry * dentry, * value for a dentry/vfsmnt pair. */ int get_dcookie(struct dentry * dentry, struct vfsmount * vfsmnt, - unsigned long * cookie) + u32 * cookie) { int err = 0; struct dcookie_struct * dcs; @@ -142,7 +142,7 @@ out: /* And here is where the userspace process can look up the cookie value * to retrieve the path. */ -asmlinkage int sys_lookup_dcookie(unsigned long cookie, char * buf, size_t len) +asmlinkage int sys_lookup_dcookie(u32 cookie, char * buf, size_t len) { char * kbuf; char * path; diff --git a/include/linux/dcookies.h b/include/linux/dcookies.h index b2ae9692dc05..7c4d3319e7d0 100644 --- a/include/linux/dcookies.h +++ b/include/linux/dcookies.h @@ -44,7 +44,7 @@ void dcookie_unregister(struct dcookie_user * user); * Returns 0 on success, with *cookie filled in */ int get_dcookie(struct dentry * dentry, struct vfsmount * vfsmnt, - unsigned long * cookie); + u32 * cookie); #else @@ -59,7 +59,7 @@ void dcookie_unregister(struct dcookie_user * user) } static inline int get_dcookie(struct dentry * dentry, - struct vfsmount * vfsmnt, unsigned long * cookie) + struct vfsmount * vfsmnt, u32 * cookie) { return -ENOSYS; } -- cgit v1.2.3 From 5d6af116099e9ba443b2c24e18cfdb0bd2f749f2 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Tue, 15 Oct 2002 04:35:16 -0700 Subject: [PATCH] futex-2.5.42-A2 This is my current futex patchset against BK-curr. It mostly includes must-have crash/correctness fixes from Martin Wirth, tested and reworked somewhat by myself: - crash fix: futex_close did not detach from the vcache. Detach cleanups. (Martin Wirth) - memory leak fix: forgotten put_page() in a rare path in __pin_page(). (Martin Wirth) - crash fix: do not do any quickcheck in unqueue_me(). (Martin, me) - correctness fix: the fastpath in __pin_page() now handles reserved pages the same way get_user_pages() does. (Martin Wirth) - queueing improvement: __attach_vcache() now uses list_add_tail() to avoid the reversal of the futex queue if a COW happens. (Martin Wirth) - simplified alignment check in sys_futex. (Martin Wirth) - comment fix: make it clear how the vcache hash quickcheck works. (me) --- include/linux/vcache.h | 2 +- kernel/futex.c | 29 +++++++++++++---------------- mm/vcache.c | 19 ++++++++----------- 3 files changed, 22 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/linux/vcache.h b/include/linux/vcache.h index d5756643332c..5708fe6a908a 100644 --- a/include/linux/vcache.h +++ b/include/linux/vcache.h @@ -18,7 +18,7 @@ extern void __attach_vcache(vcache_t *vcache, struct mm_struct *mm, void (*callback)(struct vcache_s *data, struct page *new_page)); -extern void detach_vcache(vcache_t *vcache); +extern void __detach_vcache(vcache_t *vcache); extern void invalidate_vcache(unsigned long address, struct mm_struct *mm, struct page *new_page); diff --git a/kernel/futex.c b/kernel/futex.c index d268c3c1b758..4aa2115c4d66 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -115,8 +115,9 @@ static struct page *__pin_page(unsigned long addr) * Do a quick atomic lookup first - this is the fastpath. */ page = follow_page(mm, addr, 0); - if (likely(page != NULL)) { - get_page(page); + if (likely(page != NULL)) { + if (!PageReserved(page)) + get_page(page); return page; } @@ -140,8 +141,10 @@ repeat_lookup: * check for races: */ tmp = follow_page(mm, addr, 0); - if (tmp != page) + if (tmp != page) { + put_page(page); goto repeat_lookup; + } return page; } @@ -176,6 +179,7 @@ static int futex_wake(unsigned long uaddr, int offset, int num) if (this->page == page && this->offset == offset) { list_del_init(i); + __detach_vcache(&this->vcache); tell_waiter(this); ret++; if (ret >= num) @@ -235,15 +239,15 @@ static inline int unqueue_me(struct futex_q *q) { int ret = 0; - detach_vcache(&q->vcache); - + spin_lock(&vcache_lock); spin_lock(&futex_lock); if (!list_empty(&q->list)) { list_del(&q->list); + __detach_vcache(&q->vcache); ret = 1; } spin_unlock(&futex_lock); - + spin_unlock(&vcache_lock); return ret; } @@ -314,13 +318,7 @@ static int futex_close(struct inode *inode, struct file *filp) { struct futex_q *q = filp->private_data; - spin_lock(&futex_lock); - if (!list_empty(&q->list)) { - list_del(&q->list); - /* Noone can be polling on us now. */ - BUG_ON(waitqueue_active(&q->waiters)); - } - spin_unlock(&futex_lock); + unqueue_me(q); unpin_page(q->page); kfree(filp->private_data); return 0; @@ -436,9 +434,8 @@ asmlinkage int sys_futex(unsigned long uaddr, int op, int val, struct timespec * pos_in_page = uaddr % PAGE_SIZE; - /* Must be "naturally" aligned, and not on page boundary. */ - if ((pos_in_page % __alignof__(int)) != 0 - || pos_in_page + sizeof(int) > PAGE_SIZE) + /* Must be "naturally" aligned */ + if (pos_in_page % sizeof(int)) return -EINVAL; switch (op) { diff --git a/mm/vcache.c b/mm/vcache.c index ea6bc9d2b259..599e0f25490d 100644 --- a/mm/vcache.c +++ b/mm/vcache.c @@ -41,14 +41,12 @@ void __attach_vcache(vcache_t *vcache, hash_head = hash_vcache(address, mm); - list_add(&vcache->hash_entry, hash_head); + list_add_tail(&vcache->hash_entry, hash_head); } -void detach_vcache(vcache_t *vcache) +void __detach_vcache(vcache_t *vcache) { - spin_lock(&vcache_lock); - list_del(&vcache->hash_entry); - spin_unlock(&vcache_lock); + list_del_init(&vcache->hash_entry); } void invalidate_vcache(unsigned long address, struct mm_struct *mm, @@ -61,12 +59,11 @@ void invalidate_vcache(unsigned long address, struct mm_struct *mm, hash_head = hash_vcache(address, mm); /* - * This is safe, because this path is called with the mm - * semaphore read-held, and the add/remove path calls with the - * mm semaphore write-held. So while other mm's might add new - * entries in parallel, and *this* mm is locked out, so if the - * list is empty now then we do not have to take the vcache - * lock to see it's really empty. + * This is safe, because this path is called with the pagetable + * lock held. So while other mm's might add new entries in + * parallel, *this* mm is locked out, so if the list is empty + * now then we do not have to take the vcache lock to see it's + * really empty. */ if (likely(list_empty(hash_head))) return; -- cgit v1.2.3 From b354d9171b5c04720daa2c1f993a10f15e372286 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 15 Oct 2002 05:30:42 -0700 Subject: [PATCH] A basic NFSv4 client for 2.5.x Further cleanups Separate the static and dynamic filesystem data retrieval calls as per the NFSv3 spec. This also simplifies things for NFSv4, since many of the attributes in the fsinfo+fstat combined call are not mandatory to implement. --- fs/nfs/inode.c | 55 ++++++++++++++++++++++++++----------------------- fs/nfs/nfs2xdr.c | 29 +++++--------------------- fs/nfs/nfs3proc.c | 40 ++++++++++++++++++++++++++--------- fs/nfs/nfs3xdr.c | 18 +++++++--------- fs/nfs/proc.c | 53 ++++++++++++++++++++++++++++++++++++++++++++--- include/linux/nfs_xdr.h | 28 +++++++++++++++++++++---- 6 files changed, 146 insertions(+), 77 deletions(-) (limited to 'include') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index f6cfe092fb13..940173f909b8 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -240,7 +240,13 @@ int nfs_sb_init(struct super_block *sb) { struct nfs_server *server; struct inode *root_inode = NULL; - struct nfs_fsinfo fsinfo; + struct nfs_fattr fattr; + struct nfs_fsinfo fsinfo = { + .fattr = &fattr, + }; + struct nfs_pathconf pathinfo = { + .fattr = &fattr, + }; /* We probably want something more informative here */ snprintf(sb->s_id, sizeof(sb->s_id), "%x:%x", MAJOR(sb->s_dev), MINOR(sb->s_dev)); @@ -265,31 +271,27 @@ int nfs_sb_init(struct super_block *sb) sb->s_root->d_op = &nfs_dentry_operations; /* Get some general file system info */ - if (server->rpc_ops->statfs(server, &server->fh, &fsinfo) >= 0) { - if (server->namelen == 0) - server->namelen = fsinfo.namelen; - } else { + if (server->rpc_ops->fsinfo(server, &server->fh, &fsinfo) < 0) { printk(KERN_NOTICE "NFS: cannot retrieve file system info.\n"); goto out_no_root; } - + if (server->namelen == 0 && + server->rpc_ops->pathconf(server, &server->fh, &pathinfo) >= 0) + server->namelen = pathinfo.max_namelen; /* Work out a lot of parameters */ if (server->rsize == 0) server->rsize = nfs_block_size(fsinfo.rtpref, NULL); if (server->wsize == 0) server->wsize = nfs_block_size(fsinfo.wtpref, NULL); - /* NFSv3: we don't have bsize, but rather rtmult and wtmult... */ - if (!fsinfo.bsize) - fsinfo.bsize = (fsinfo.rtmult>fsinfo.wtmult) ? fsinfo.rtmult : fsinfo.wtmult; - /* Also make sure we don't go below rsize/wsize since - * RPC calls are expensive */ - if (fsinfo.bsize < server->rsize) - fsinfo.bsize = server->rsize; - if (fsinfo.bsize < server->wsize) - fsinfo.bsize = server->wsize; - - if (sb->s_blocksize == 0) - sb->s_blocksize = nfs_block_bits(fsinfo.bsize, &sb->s_blocksize_bits); + if (sb->s_blocksize == 0) { + if (fsinfo.wtmult == 0) { + sb->s_blocksize = 512; + sb->s_blocksize_bits = 9; + } else + sb->s_blocksize = nfs_block_bits(fsinfo.wtmult, + &sb->s_blocksize_bits); + } + if (fsinfo.rtmax >= 512 && server->rsize > fsinfo.rtmax) server->rsize = nfs_block_size(fsinfo.rtmax, NULL); if (fsinfo.wtmax >= 512 && server->wsize > fsinfo.wtmax) @@ -472,29 +474,30 @@ nfs_statfs(struct super_block *sb, struct statfs *buf) struct nfs_server *server = NFS_SB(sb); unsigned char blockbits; unsigned long blockres; - struct nfs_fsinfo res; + struct nfs_fh *rootfh = NFS_FH(sb->s_root->d_inode); + struct nfs_fattr fattr; + struct nfs_fsstat res = { + .fattr = &fattr, + }; int error; lock_kernel(); - error = server->rpc_ops->statfs(server, NFS_FH(sb->s_root->d_inode), &res); + error = server->rpc_ops->statfs(server, rootfh, &res); buf->f_type = NFS_SUPER_MAGIC; if (error < 0) goto out_err; - if (res.bsize == 0) - res.bsize = sb->s_blocksize; - buf->f_bsize = nfs_block_bits(res.bsize, &blockbits); + buf->f_bsize = sb->s_blocksize; + blockbits = sb->s_blocksize_bits; blockres = (1 << blockbits) - 1; buf->f_blocks = (res.tbytes + blockres) >> blockbits; buf->f_bfree = (res.fbytes + blockres) >> blockbits; buf->f_bavail = (res.abytes + blockres) >> blockbits; buf->f_files = res.tfiles; buf->f_ffree = res.afiles; - if (res.namelen == 0 || res.namelen > server->namelen) - res.namelen = server->namelen; - buf->f_namelen = res.namelen; + buf->f_namelen = server->namelen; out: unlock_kernel(); diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 8dc92b8b3a1c..8e652afdfea4 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -596,37 +596,18 @@ nfs_xdr_writeres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res) * Decode STATFS reply */ static int -nfs_xdr_statfsres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res) +nfs_xdr_statfsres(struct rpc_rqst *req, u32 *p, struct nfs2_fsstat *res) { int status; - u32 xfer_size; if ((status = ntohl(*p++))) return -nfs_stat_to_errno(status); - /* For NFSv2, we more or less have to guess the preferred - * read/write/readdir sizes from the single 'transfer size' - * value. - */ - xfer_size = ntohl(*p++); /* tsize */ - res->rtmax = 8 * 1024; - res->rtpref = xfer_size; - res->rtmult = xfer_size; - res->wtmax = 8 * 1024; - res->wtpref = xfer_size; - res->wtmult = xfer_size; - res->dtpref = PAGE_CACHE_SIZE; - res->maxfilesize = 0x7FFFFFFF; /* just a guess */ + res->tsize = ntohl(*p++); res->bsize = ntohl(*p++); - - res->tbytes = ntohl(*p++) * res->bsize; - res->fbytes = ntohl(*p++) * res->bsize; - res->abytes = ntohl(*p++) * res->bsize; - res->tfiles = 0; - res->ffiles = 0; - res->afiles = 0; - res->namelen = 0; - + res->blocks = ntohl(*p++); + res->bfree = ntohl(*p++); + res->bavail = ntohl(*p++); return 0; } diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 1ddb51374cba..790c27ead44f 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -639,24 +639,42 @@ nfs3_proc_mknod(struct inode *dir, struct qstr *name, struct iattr *sattr, return status; } -/* - * This is a combo call of fsstat and fsinfo - */ static int nfs3_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsinfo *info) + struct nfs_fsstat *stat) { int status; dprintk("NFS call fsstat\n"); - memset((char *)info, 0, sizeof(*info)); - status = rpc_call(server->client, NFS3PROC_FSSTAT, fhandle, info, 0); - if (status < 0) - goto error; + stat->fattr->valid = 0; + status = rpc_call(server->client, NFS3PROC_FSSTAT, fhandle, stat, 0); + dprintk("NFS reply statfs: %d\n", status); + return status; +} + +static int +nfs3_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_fsinfo *info) +{ + int status; + + dprintk("NFS call fsinfo\n"); + info->fattr->valid = 0; status = rpc_call(server->client, NFS3PROC_FSINFO, fhandle, info, 0); + dprintk("NFS reply fsinfo: %d\n", status); + return status; +} -error: - dprintk("NFS reply statfs: %d\n", status); +static int +nfs3_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_pathconf *info) +{ + int status; + + dprintk("NFS call pathconf\n"); + info->fattr->valid = 0; + status = rpc_call(server->client, NFS3PROC_PATHCONF, fhandle, info, 0); + dprintk("NFS reply pathconf: %d\n", status); return status; } @@ -824,6 +842,8 @@ struct nfs_rpc_ops nfs_v3_clientops = { .readdir = nfs3_proc_readdir, .mknod = nfs3_proc_mknod, .statfs = nfs3_proc_statfs, + .fsinfo = nfs3_proc_fsinfo, + .pathconf = nfs3_proc_pathconf, .decode_dirent = nfs3_decode_dirent, .read_setup = nfs3_proc_read_setup, .write_setup = nfs3_proc_write_setup, diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index b0c77b19fff9..2a813fb65365 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -912,14 +912,13 @@ nfs3_xdr_linkres(struct rpc_rqst *req, u32 *p, struct nfs3_linkres *res) * Decode FSSTAT reply */ static int -nfs3_xdr_fsstatres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res) +nfs3_xdr_fsstatres(struct rpc_rqst *req, u32 *p, struct nfs_fsstat *res) { - struct nfs_fattr dummy; int status; status = ntohl(*p++); - p = xdr_decode_post_op_attr(p, &dummy); + p = xdr_decode_post_op_attr(p, res->fattr); if (status != 0) return -nfs_stat_to_errno(status); @@ -940,12 +939,11 @@ nfs3_xdr_fsstatres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res) static int nfs3_xdr_fsinfores(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res) { - struct nfs_fattr dummy; int status; status = ntohl(*p++); - p = xdr_decode_post_op_attr(p, &dummy); + p = xdr_decode_post_op_attr(p, res->fattr); if (status != 0) return -nfs_stat_to_errno(status); @@ -959,6 +957,7 @@ nfs3_xdr_fsinfores(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res) p = xdr_decode_hyper(p, &res->maxfilesize); /* ignore time_delta and properties */ + res->lease_time = 0; return 0; } @@ -966,18 +965,17 @@ nfs3_xdr_fsinfores(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res) * Decode PATHCONF reply */ static int -nfs3_xdr_pathconfres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res) +nfs3_xdr_pathconfres(struct rpc_rqst *req, u32 *p, struct nfs_pathconf *res) { - struct nfs_fattr dummy; int status; status = ntohl(*p++); - p = xdr_decode_post_op_attr(p, &dummy); + p = xdr_decode_post_op_attr(p, res->fattr); if (status != 0) return -nfs_stat_to_errno(status); - res->linkmax = ntohl(*p++); - res->namelen = ntohl(*p++); + res->max_link = ntohl(*p++); + res->max_namelen = ntohl(*p++); /* ignore remaining fields */ return 0; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 2ad13ec4cd27..a5a1c373444d 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -460,17 +460,62 @@ nfs_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, static int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fsinfo *info) + struct nfs_fsstat *stat) { + struct nfs2_fsstat fsinfo; int status; dprintk("NFS call statfs\n"); - memset((char *)info, 0, sizeof(*info)); - status = rpc_call(server->client, NFSPROC_STATFS, fhandle, info, 0); + stat->fattr->valid = 0; + status = rpc_call(server->client, NFSPROC_STATFS, fhandle, &fsinfo, 0); dprintk("NFS reply statfs: %d\n", status); + if (status) + goto out; + stat->tbytes = (u64)fsinfo.blocks * fsinfo.bsize; + stat->fbytes = (u64)fsinfo.bfree * fsinfo.bsize; + stat->abytes = (u64)fsinfo.bavail * fsinfo.bsize; + stat->tfiles = 0; + stat->ffiles = 0; + stat->afiles = 0; +out: + return status; +} + +static int +nfs_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_fsinfo *info) +{ + struct nfs2_fsstat fsinfo; + int status; + + dprintk("NFS call fsinfo\n"); + info->fattr->valid = 0; + status = rpc_call(server->client, NFSPROC_STATFS, fhandle, &info, 0); + dprintk("NFS reply fsinfo: %d\n", status); + if (status) + goto out; + info->rtmax = NFS_MAXDATA; + info->rtpref = fsinfo.tsize; + info->rtmult = fsinfo.bsize; + info->wtmax = NFS_MAXDATA; + info->wtpref = fsinfo.tsize; + info->wtmult = fsinfo.bsize; + info->dtpref = fsinfo.tsize; + info->maxfilesize = 0x7FFFFFFF; + info->lease_time = 0; +out: return status; } +static int +nfs_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_pathconf *info) +{ + info->max_link = 0; + info->max_namelen = NFS2_MAXNAMLEN; + return 0; +} + extern u32 * nfs_decode_dirent(u32 *, struct nfs_entry *, int); static void @@ -590,6 +635,8 @@ struct nfs_rpc_ops nfs_v2_clientops = { .readdir = nfs_proc_readdir, .mknod = nfs_proc_mknod, .statfs = nfs_proc_statfs, + .fsinfo = nfs_proc_fsinfo, + .pathconf = nfs_proc_pathconf, .decode_dirent = nfs_decode_dirent, .read_setup = nfs_proc_read_setup, .write_setup = nfs_proc_write_setup, diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index b71b1b217c70..e542fe6982c5 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -50,6 +50,7 @@ struct nfs_fattr { * Info on the file system */ struct nfs_fsinfo { + struct nfs_fattr *fattr; /* Post-op attributes */ __u32 rtmax; /* max. read transfer size */ __u32 rtpref; /* pref. read transfer size */ __u32 rtmult; /* reads should be multiple of this */ @@ -58,16 +59,31 @@ struct nfs_fsinfo { __u32 wtmult; /* writes should be multiple of this */ __u32 dtpref; /* pref. readdir transfer size */ __u64 maxfilesize; - __u64 bsize; /* block size */ + __u32 lease_time; /* in seconds */ +}; + +struct nfs_fsstat { + struct nfs_fattr *fattr; /* Post-op attributes */ __u64 tbytes; /* total size in bytes */ __u64 fbytes; /* # of free bytes */ __u64 abytes; /* # of bytes available to user */ __u64 tfiles; /* # of files */ __u64 ffiles; /* # of free files */ __u64 afiles; /* # of files available to user */ - __u32 linkmax;/* max # of hard links */ - __u32 namelen;/* max name length */ - __u32 lease_time; /* in seconds */ +}; + +struct nfs2_fsstat { + __u32 tsize; /* Server transfer size */ + __u32 bsize; /* Filesystem block size */ + __u32 blocks; /* No. of "bsize" blocks on filesystem */ + __u32 bfree; /* No. of free "bsize" blocks */ + __u32 bavail; /* No. of available "bsize" blocks */ +}; + +struct nfs_pathconf { + struct nfs_fattr *fattr; /* Post-op attributes */ + __u32 max_link; /* max # of hard links */ + __u32 max_namelen; /* max name length */ }; /* @@ -391,7 +407,11 @@ struct nfs_rpc_ops { int (*mknod) (struct inode *, struct qstr *, struct iattr *, dev_t, struct nfs_fh *, struct nfs_fattr *); int (*statfs) (struct nfs_server *, struct nfs_fh *, + struct nfs_fsstat *); + int (*fsinfo) (struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); + int (*pathconf) (struct nfs_server *, struct nfs_fh *, + struct nfs_pathconf *); u32 * (*decode_dirent)(u32 *, struct nfs_entry *, int plus); void (*read_setup) (struct nfs_read_data *, unsigned int count); void (*write_setup) (struct nfs_write_data *, unsigned int count, int how); -- cgit v1.2.3 From ee17e0d649279f5cc07ed24506b0fcea53dbe899 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 15 Oct 2002 05:30:48 -0700 Subject: [PATCH] A basic NFSv4 client for 2.5.x Define the new NFSv4 data structure for passing user information from the 'mount' program in nfs4_mount.h. If CONFIG_NFS_V4 is defined Add code to parse the mount structure into the superblock. Declare the NFSv4 filesystem to the VFS. --- fs/nfs/inode.c | 238 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/nfs.h | 2 +- include/linux/nfs3.h | 5 + include/linux/nfs4_mount.h | 70 +++++++++++++ include/linux/nfs_fs.h | 30 ++++++ include/linux/nfs_fs_sb.h | 10 ++ include/linux/nfs_mount.h | 8 +- 7 files changed, 361 insertions(+), 2 deletions(-) create mode 100644 include/linux/nfs4_mount.h (limited to 'include') diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 940173f909b8..685d9eb076cb 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -157,6 +158,7 @@ nfs_put_super(struct super_block *sb) lockd_down(); /* release rpc.lockd */ rpciod_down(); /* release rpciod */ + destroy_nfsv4_state(server); kfree(server->hostname); } @@ -1283,6 +1285,239 @@ static struct file_system_type nfs_fs_type = { .fs_flags = FS_ODD_RENAME, }; +#ifdef CONFIG_NFS_V4 + +static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, int silent) +{ + struct nfs_server *server; + struct rpc_xprt *xprt = NULL; + struct rpc_clnt *clnt = NULL; + struct rpc_timeout timeparms; + rpc_authflavor_t authflavour; + int proto, err = -EIO; + + sb->s_blocksize_bits = 0; + sb->s_blocksize = 0; + server = NFS_SB(sb); + if (data->rsize != 0) + server->rsize = nfs_block_size(data->rsize, NULL); + if (data->wsize != 0) + server->wsize = nfs_block_size(data->wsize, NULL); + server->flags = data->flags & NFS_MOUNT_FLAGMASK; + + /* NFSv4 doesn't use NLM locking */ + server->flags |= NFS_MOUNT_NONLM; + + server->acregmin = data->acregmin*HZ; + server->acregmax = data->acregmax*HZ; + server->acdirmin = data->acdirmin*HZ; + server->acdirmax = data->acdirmax*HZ; + + server->rpc_ops = &nfs_v4_clientops; + /* Initialize timeout values */ + + timeparms.to_initval = data->timeo * HZ / 10; + timeparms.to_retries = data->retrans; + timeparms.to_exponential = 1; + if (!timeparms.to_retries) + timeparms.to_retries = 5; + + proto = data->proto; + /* Which IP protocol do we use? */ + switch (proto) { + case IPPROTO_TCP: + timeparms.to_maxval = RPC_MAX_TCP_TIMEOUT; + if (!timeparms.to_initval) + timeparms.to_initval = 600 * HZ / 10; + break; + case IPPROTO_UDP: + timeparms.to_maxval = RPC_MAX_UDP_TIMEOUT; + if (!timeparms.to_initval) + timeparms.to_initval = 11 * HZ / 10; + break; + default: + return -EINVAL; + } + + /* Now create transport and client */ + xprt = xprt_create_proto(proto, &server->addr, &timeparms); + if (xprt == NULL) { + printk(KERN_WARNING "NFS: cannot create RPC transport.\n"); + goto out_fail; + } + + authflavour = RPC_AUTH_UNIX; + if (data->auth_flavourlen != 0) { + if (data->auth_flavourlen > 1) + printk(KERN_INFO "NFS: cannot yet deal with multiple auth flavours.\n"); + if (copy_from_user(authflavour, data->auth_flavours, sizeof(authflavour))) { + err = -EFAULT; + goto out_fail; + } + } + clnt = rpc_create_client(xprt, server->hostname, &nfs_program, + server->rpc_ops->version, authflavour); + if (clnt == NULL) { + printk(KERN_WARNING "NFS: cannot create RPC client.\n"); + xprt_destroy(xprt); + goto out_fail; + } + + clnt->cl_intr = (server->flags & NFS4_MOUNT_INTR) ? 1 : 0; + clnt->cl_softrtry = (server->flags & NFS4_MOUNT_SOFT) ? 1 : 0; + clnt->cl_chatty = 1; + server->client = clnt; + + /* Fire up rpciod if not yet running */ + if (rpciod_up() != 0) { + printk(KERN_WARNING "NFS: couldn't start rpciod!\n"); + goto out_shutdown; + } + + if (create_nfsv4_state(server, data)) + goto out_shutdown; + + err = nfs_sb_init(sb); + if (err == 0) + return 0; + rpciod_down(); + destroy_nfsv4_state(server); +out_shutdown: + rpc_shutdown_client(server->client); +out_fail: + return err; +} + +static int nfs4_compare_super(struct super_block *sb, void *data) +{ + struct nfs_server *server = data; + struct nfs_server *old = NFS_SB(sb); + + if (strcmp(server->hostname, old->hostname) != 0) + return 0; + if (strcmp(server->mnt_path, old->mnt_path) != 0) + return 0; + return 1; +} + +static void * +nfs_copy_user_string(char *dst, struct nfs_string *src, int maxlen) +{ + void *p = NULL; + + if (!src->len) + return ERR_PTR(-EINVAL); + if (src->len < maxlen) + maxlen = src->len; + if (dst == NULL) { + p = dst = kmalloc(maxlen + 1, GFP_KERNEL); + if (p == NULL) + return ERR_PTR(-ENOMEM); + } + if (copy_from_user(dst, src->data, maxlen)) { + if (p != NULL) + kfree(p); + return ERR_PTR(-EFAULT); + } + dst[maxlen] = '\0'; + return dst; +} + +static struct super_block *nfs4_get_sb(struct file_system_type *fs_type, + int flags, char *dev_name, void *raw_data) +{ + int error; + struct nfs_server *server; + struct super_block *s; + struct nfs4_mount_data *data = raw_data; + void *p; + + if (!data) { + printk("nfs_read_super: missing data argument\n"); + return ERR_PTR(-EINVAL); + } + + server = kmalloc(sizeof(struct nfs_server), GFP_KERNEL); + if (!server) + return ERR_PTR(-ENOMEM); + memset(server, 0, sizeof(struct nfs_server)); + + if (data->version != NFS4_MOUNT_VERSION) { + printk("nfs warning: mount version %s than kernel\n", + data->version < NFS_MOUNT_VERSION ? "older" : "newer"); + } + + p = nfs_copy_user_string(NULL, &data->hostname, 256); + if (IS_ERR(p)) + goto out_err; + server->hostname = p; + + p = nfs_copy_user_string(NULL, &data->mnt_path, 1024); + if (IS_ERR(p)) + goto out_err; + server->mnt_path = p; + + p = nfs_copy_user_string(server->ip_addr, &data->client_addr, + sizeof(server->ip_addr)); + if (IS_ERR(p)) + goto out_err; + + /* We now require that the mount process passes the remote address */ + if (data->host_addrlen != sizeof(server->addr)) { + s = ERR_PTR(-EINVAL); + goto out_free; + } + if (copy_from_user(&server->addr, data->host_addr, sizeof(server->addr))) { + s = ERR_PTR(-EFAULT); + goto out_free; + } + if (server->addr.sin_family != AF_INET || + server->addr.sin_addr.s_addr == INADDR_ANY) { + printk("NFS: mount program didn't pass remote IP address!\n"); + s = ERR_PTR(-EINVAL); + goto out_free; + } + + s = sget(fs_type, nfs4_compare_super, nfs_set_super, server); + + if (IS_ERR(s) || s->s_root) + goto out_free; + + s->s_flags = flags; + + error = nfs4_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0); + if (error) { + up_write(&s->s_umount); + deactivate_super(s); + return ERR_PTR(error); + } + s->s_flags |= MS_ACTIVE; + return s; +out_err: + s = (struct super_block *)p; +out_free: + if (server->mnt_path) + kfree(server->mnt_path); + if (server->hostname) + kfree(server->hostname); + kfree(server); + return s; +} + +static struct file_system_type nfs4_fs_type = { + .owner = THIS_MODULE, + .name = "nfs4", + .get_sb = nfs4_get_sb, + .kill_sb = nfs_kill_super, + .fs_flags = FS_ODD_RENAME, +}; +#define register_nfs4fs() register_filesystem(&nfs4_fs_type) +#define unregister_nfs4fs() unregister_filesystem(&nfs4_fs_type) +#else +#define register_nfs4fs() (0) +#define unregister_nfs4fs() +#endif + extern int nfs_init_nfspagecache(void); extern void nfs_destroy_nfspagecache(void); extern int nfs_init_readpagecache(void); @@ -1374,6 +1609,8 @@ static int __init init_nfs_fs(void) err = register_filesystem(&nfs_fs_type); if (err) goto out; + if ((err = register_nfs4fs()) != 0) + goto out; return 0; out: rpc_proc_unregister("nfs"); @@ -1398,6 +1635,7 @@ static void __exit exit_nfs_fs(void) rpc_proc_unregister("nfs"); #endif unregister_filesystem(&nfs_fs_type); + unregister_nfs4fs(); } /* Not quite true; I just maintain it */ diff --git a/include/linux/nfs.h b/include/linux/nfs.h index 181e8decebfc..d99650a19b55 100644 --- a/include/linux/nfs.h +++ b/include/linux/nfs.h @@ -120,7 +120,7 @@ enum nfs_ftype { /* * This is the kernel NFS client file handle representation */ -#define NFS_MAXFHSIZE 64 +#define NFS_MAXFHSIZE 128 struct nfs_fh { unsigned short size; unsigned char data[NFS_MAXFHSIZE]; diff --git a/include/linux/nfs3.h b/include/linux/nfs3.h index 359c73e00841..7f11fa589207 100644 --- a/include/linux/nfs3.h +++ b/include/linux/nfs3.h @@ -59,6 +59,11 @@ enum nfs3_ftype { NF3BAD = 8 }; +struct nfs3_fh { + unsigned short size; + unsigned char data[NFS3_FHSIZE]; +}; + #define NFS3_VERSION 3 #define NFS3PROC_NULL 0 #define NFS3PROC_GETATTR 1 diff --git a/include/linux/nfs4_mount.h b/include/linux/nfs4_mount.h new file mode 100644 index 000000000000..9a782c2bbdd3 --- /dev/null +++ b/include/linux/nfs4_mount.h @@ -0,0 +1,70 @@ +#ifndef _LINUX_NFS4_MOUNT_H +#define _LINUX_NFS4_MOUNT_H + +/* + * linux/include/linux/nfs4_mount.h + * + * Copyright (C) 2002 Trond Myklebust + * + * structure passed from user-space to kernel-space during an nfsv4 mount + */ + +/* + * WARNING! Do not delete or change the order of these fields. If + * a new field is required then add it to the end. The version field + * tracks which fields are present. This will ensure some measure of + * mount-to-kernel version compatibility. Some of these aren't used yet + * but here they are anyway. + */ +#define NFS4_MOUNT_VERSION 1 + +struct nfs_string { + unsigned int len; + const char* data; +}; + +struct nfs4_mount_data { + int version; /* 1 */ + int flags; /* 1 */ + int rsize; /* 1 */ + int wsize; /* 1 */ + int timeo; /* 1 */ + int retrans; /* 1 */ + int acregmin; /* 1 */ + int acregmax; /* 1 */ + int acdirmin; /* 1 */ + int acdirmax; /* 1 */ + + /* see the definition of 'struct clientaddr4' in RFC3010 */ + struct nfs_string client_addr; /* 1 */ + + /* Mount path */ + struct nfs_string mnt_path; /* 1 */ + + /* Server details */ + struct nfs_string hostname; /* 1 */ + /* Server IP address */ + unsigned int host_addrlen; /* 1 */ + struct sockaddr* host_addr; /* 1 */ + + /* Transport protocol to use */ + int proto; /* 1 */ + + /* Pseudo-flavours to use for authentication. See RFC2623 */ + int auth_flavourlen; /* 1 */ + int *auth_flavours; /* 1 */ +}; + +/* bits in the flags field */ +/* Note: the fields that correspond to existing NFSv2/v3 mount options + * should mirror the values from include/linux/nfs_mount.h + */ + +#define NFS4_MOUNT_SOFT 0x0001 /* 1 */ +#define NFS4_MOUNT_INTR 0x0002 /* 1 */ +#define NFS4_MOUNT_NOCTO 0x0010 /* 1 */ +#define NFS4_MOUNT_NOAC 0x0020 /* 1 */ +#define NFS4_MOUNT_STRICTLOCK 0x1000 /* 1 */ +#define NFS4_MOUNT_FLAGMASK 0xFFFF + +#endif diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 4c35f7cbb97c..76ab4ecc3ea8 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -472,6 +472,36 @@ extern void * nfs_root_data(void); #define NFS_JUKEBOX_RETRY_TIME (5 * HZ) +#ifdef CONFIG_NFS_V4 + +extern struct nfs4_client *nfs4_get_client(void); +extern void nfs4_put_client(struct nfs4_client *clp); + +struct nfs4_mount_data; +static inline int +create_nfsv4_state(struct nfs_server *server, struct nfs4_mount_data *data) +{ + server->nfs4_state = NULL; + return 0; +} + +static inline void +destroy_nfsv4_state(struct nfs_server *server) +{ + if (server->mnt_path) { + kfree(server->mnt_path); + server->mnt_path = NULL; + } + if (server->nfs4_state) { + nfs4_put_client(server->nfs4_state); + server->nfs4_state = NULL; + } +} +#else +#define create_nfsv4_state(server, data) 0 +#define destroy_nfsv4_state(server) do { } while (0) +#endif + #endif /* __KERNEL__ */ /* diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 080c98fed1dd..d171608d7105 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -30,6 +30,16 @@ struct nfs_server { lru_busy; struct nfs_fh fh; struct sockaddr_in addr; +#if CONFIG_NFS_V4 + /* Our own IP address, as a null-terminated string. + * This is used to generate the clientid, and the callback address. + */ + char ip_addr[16]; + char * mnt_path; + struct nfs4_client * nfs4_state; /* all NFSv4 state starts here */ + unsigned long lease_time; /* in jiffies */ + unsigned long last_renewal; /* in jiffies */ +#endif }; /* Server capabilities */ diff --git a/include/linux/nfs_mount.h b/include/linux/nfs_mount.h index 2b552936eeca..223ed3462064 100644 --- a/include/linux/nfs_mount.h +++ b/include/linux/nfs_mount.h @@ -10,6 +10,8 @@ */ #include #include +#include +#include /* * WARNING! Do not delete or change the order of these fields. If @@ -37,7 +39,7 @@ struct nfs_mount_data { char hostname[256]; /* 1 */ int namlen; /* 2 */ unsigned int bsize; /* 3 */ - struct nfs_fh root; /* 4 */ + struct nfs3_fh root; /* 4 */ }; /* bits in the flags field */ @@ -53,6 +55,10 @@ struct nfs_mount_data { #define NFS_MOUNT_KERBEROS 0x0100 /* 3 */ #define NFS_MOUNT_NONLM 0x0200 /* 3 */ #define NFS_MOUNT_BROKEN_SUID 0x0400 /* 4 */ +#if 0 +#define NFS_MOUNT_STRICTLOCK 0x1000 /* reserved for NFSv4 */ +#define NFS_MOUNT_SECFLAVOUR 0x2000 /* reserved */ +#endif #define NFS_MOUNT_FLAGMASK 0xFFFF #endif -- cgit v1.2.3 From bf5344dc4c1c97bd8c837f8fa62315f13784f94d Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 15 Oct 2002 05:30:53 -0700 Subject: [PATCH] A basic NFSv4 client for 2.5.x Now that all the hooks are in place, this large patch imports all of the new code for the NFSv4 client. nfs4proc.c - procedure vectors nfs4xdr.c - XDR nfs4state.c - state bookkeeping (very minimal for now) nfs4renewd.c - a daemon (implemented as an rpc_task) to keep state from expiring on the server Note: The RPCSEC_GSS authentication code is not yet included here. For the moment we make do with AUTH_UNIX aka. AUTH_SYS. Neither is the code to do upcalls to userland in order to do uid/gid <-> name mappings. Instead, stubs have been added to translate everything to 'nobody:nobody' == '-2:-2' --- fs/nfs/Makefile | 1 + fs/nfs/file.c | 6 + fs/nfs/inode.c | 7 +- fs/nfs/nfs4proc.c | 1577 +++++++++++++++++++++++++++++++++++++++++ fs/nfs/nfs4renewd.c | 110 +++ fs/nfs/nfs4state.c | 81 +++ fs/nfs/nfs4xdr.c | 1777 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/nfs_fs.h | 19 + include/linux/nfs_xdr.h | 234 ++++++- 9 files changed, 3807 insertions(+), 5 deletions(-) create mode 100644 fs/nfs/nfs4proc.c create mode 100644 fs/nfs/nfs4renewd.c create mode 100644 fs/nfs/nfs4state.c create mode 100644 fs/nfs/nfs4xdr.c (limited to 'include') diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 836322c2be06..c098a522553b 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -8,6 +8,7 @@ nfs-y := dir.o file.o flushd.o inode.o nfs2xdr.o pagelist.o \ proc.o read.o symlink.o unlink.o write.o nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o +nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs-$(CONFIG_NFS_DIRECTIO) += direct.o nfs-objs := $(nfs-y) diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 3443f647ed2f..f02b7c9c7f36 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -259,6 +259,12 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl) if (!inode) return -EINVAL; + /* This will be in a forthcoming patch. */ + if (NFS_PROTO(inode)->version == 4) { + printk(KERN_INFO "NFS: file locking over NFSv4 is not yet supported\n"); + return -EIO; + } + /* No mandatory locks over NFS */ if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) return -ENOLCK; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 685d9eb076cb..39027f2af310 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -77,8 +77,13 @@ static struct rpc_version * nfs_version[] = { NULL, NULL, &nfs_version2, -#ifdef CONFIG_NFS_V3 +#if defined(CONFIG_NFS_V3) &nfs_version3, +#elif defined(CONFIG_NFS_V4) + NULL, +#endif +#if defined(CONFIG_NFS_V4) + &nfs_version4, #endif }; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c new file mode 100644 index 000000000000..8608fd9b3a30 --- /dev/null +++ b/fs/nfs/nfs4proc.c @@ -0,0 +1,1577 @@ +/* + * fs/nfs/nfs4proc.c + * + * Client-side procedure declarations for NFSv4. + * + * Copyright (c) 2002 The Regents of the University of Michigan. + * All rights reserved. + * + * Kendrick Smith + * Andy Adamson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' 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 THE REGENTS OR CONTRIBUTORS 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NFSDBG_FACILITY NFSDBG_PROC + +#define GET_OP(cp,name) &cp->ops[cp->req_nops].u.name +#define OPNUM(cp) cp->ops[cp->req_nops].opnum + +extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus); + +static nfs4_stateid zero_stateid = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static spinlock_t renew_lock = SPIN_LOCK_UNLOCKED; + +static void +nfs4_setup_compound(struct nfs4_compound *cp, struct nfs4_op *ops, + struct nfs_server *server, char *tag) +{ + memset(cp, 0, sizeof(*cp)); + cp->ops = ops; + cp->server = server; + +#if NFS4_DEBUG + cp->taglen = strlen(tag); + cp->tag = tag; +#endif +} + +static void +nfs4_setup_access(struct nfs4_compound *cp, u32 req_access, u32 *resp_supported, u32 *resp_access) +{ + struct nfs4_access *access = GET_OP(cp, access); + + access->ac_req_access = req_access; + access->ac_resp_supported = resp_supported; + access->ac_resp_access = resp_access; + + OPNUM(cp) = OP_ACCESS; + cp->req_nops++; +} + +static void +nfs4_setup_close(struct nfs4_compound *cp, nfs4_stateid stateid, u32 seqid) +{ + struct nfs4_close *close = GET_OP(cp, close); + + close->cl_stateid = stateid; + close->cl_seqid = seqid; + + OPNUM(cp) = OP_CLOSE; + cp->req_nops++; + cp->renew_index = cp->req_nops; +} + +static void +nfs4_setup_commit(struct nfs4_compound *cp, u64 start, u32 len, struct nfs_writeverf *verf) +{ + struct nfs4_commit *commit = GET_OP(cp, commit); + + commit->co_start = start; + commit->co_len = len; + commit->co_verifier = verf; + + OPNUM(cp) = OP_COMMIT; + cp->req_nops++; +} + +static void +nfs4_setup_create_dir(struct nfs4_compound *cp, struct qstr *name, + struct iattr *sattr, struct nfs4_change_info *info) +{ + struct nfs4_create *create = GET_OP(cp, create); + + create->cr_ftype = NF4DIR; + create->cr_namelen = name->len; + create->cr_name = name->name; + create->cr_attrs = sattr; + create->cr_cinfo = info; + + OPNUM(cp) = OP_CREATE; + cp->req_nops++; +} + +static void +nfs4_setup_create_symlink(struct nfs4_compound *cp, struct qstr *name, + struct qstr *linktext, struct iattr *sattr, + struct nfs4_change_info *info) +{ + struct nfs4_create *create = GET_OP(cp, create); + + create->cr_ftype = NF4LNK; + create->cr_textlen = linktext->len; + create->cr_text = linktext->name; + create->cr_namelen = name->len; + create->cr_name = name->name; + create->cr_attrs = sattr; + create->cr_cinfo = info; + + OPNUM(cp) = OP_CREATE; + cp->req_nops++; +} + +static void +nfs4_setup_create_special(struct nfs4_compound *cp, struct qstr *name, + dev_t dev, struct iattr *sattr, + struct nfs4_change_info *info) +{ + int mode = sattr->ia_mode; + struct nfs4_create *create = GET_OP(cp, create); + + BUG_ON(!(sattr->ia_valid & ATTR_MODE)); + BUG_ON(!S_ISFIFO(mode) && !S_ISBLK(mode) && !S_ISCHR(mode) && !S_ISSOCK(mode)); + + if (S_ISFIFO(mode)) + create->cr_ftype = NF4FIFO; + else if (S_ISBLK(mode)) { + create->cr_ftype = NF4BLK; + create->cr_specdata1 = MAJOR(dev); + create->cr_specdata2 = MINOR(dev); + } + else if (S_ISCHR(mode)) { + create->cr_ftype = NF4CHR; + create->cr_specdata1 = MAJOR(dev); + create->cr_specdata2 = MINOR(dev); + } + else + create->cr_ftype = NF4SOCK; + + create->cr_namelen = name->len; + create->cr_name = name->name; + create->cr_attrs = sattr; + create->cr_cinfo = info; + + OPNUM(cp) = OP_CREATE; + cp->req_nops++; +} + +/* + * This is our standard bitmap for GETATTR requests. + */ +u32 nfs4_fattr_bitmap[2] = { + FATTR4_WORD0_TYPE + | FATTR4_WORD0_CHANGE + | FATTR4_WORD0_SIZE + | FATTR4_WORD0_FSID + | FATTR4_WORD0_FILEID, + FATTR4_WORD1_MODE + | FATTR4_WORD1_NUMLINKS + | FATTR4_WORD1_OWNER + | FATTR4_WORD1_OWNER_GROUP + | FATTR4_WORD1_RAWDEV + | FATTR4_WORD1_SPACE_USED + | FATTR4_WORD1_TIME_ACCESS + | FATTR4_WORD1_TIME_METADATA + | FATTR4_WORD1_TIME_MODIFY +}; + +u32 nfs4_statfs_bitmap[2] = { + FATTR4_WORD0_FILES_AVAIL + | FATTR4_WORD0_FILES_FREE + | FATTR4_WORD0_FILES_TOTAL, + FATTR4_WORD1_SPACE_AVAIL + | FATTR4_WORD1_SPACE_FREE + | FATTR4_WORD1_SPACE_TOTAL +}; + +u32 nfs4_fsinfo_bitmap[2] = { + FATTR4_WORD0_MAXFILESIZE + | FATTR4_WORD0_MAXREAD + | FATTR4_WORD0_MAXWRITE + | FATTR4_WORD0_LEASE_TIME, + 0 +}; + +u32 nfs4_pathconf_bitmap[2] = { + FATTR4_WORD0_MAXLINK + | FATTR4_WORD0_MAXNAME, + 0 +}; + +/* mount bitmap: fattr bitmap + lease time */ +u32 nfs4_mount_bitmap[2] = { + FATTR4_WORD0_TYPE + | FATTR4_WORD0_CHANGE + | FATTR4_WORD0_SIZE + | FATTR4_WORD0_FSID + | FATTR4_WORD0_FILEID + | FATTR4_WORD0_LEASE_TIME, + FATTR4_WORD1_MODE + | FATTR4_WORD1_NUMLINKS + | FATTR4_WORD1_OWNER + | FATTR4_WORD1_OWNER_GROUP + | FATTR4_WORD1_RAWDEV + | FATTR4_WORD1_SPACE_USED + | FATTR4_WORD1_TIME_ACCESS + | FATTR4_WORD1_TIME_METADATA + | FATTR4_WORD1_TIME_MODIFY +}; + +static inline void +__nfs4_setup_getattr(struct nfs4_compound *cp, u32 *bitmap, + struct nfs_fattr *fattr, + struct nfs_fsstat *fsstat, + struct nfs_fsinfo *fsinfo, + struct nfs_pathconf *pathconf, + u32 *bmres) +{ + struct nfs4_getattr *getattr = GET_OP(cp, getattr); + + getattr->gt_bmval = bitmap; + getattr->gt_attrs = fattr; + getattr->gt_fsstat = fsstat; + getattr->gt_fsinfo = fsinfo; + getattr->gt_pathconf = pathconf; + getattr->gt_bmres = bmres; + + OPNUM(cp) = OP_GETATTR; + cp->req_nops++; +} + +static void +nfs4_setup_getattr(struct nfs4_compound *cp, + struct nfs_fattr *fattr, + u32 *bmres) +{ + __nfs4_setup_getattr(cp, nfs4_fattr_bitmap, fattr, + NULL, NULL, NULL, bmres); +} + +static void +nfs4_setup_getrootattr(struct nfs4_compound *cp, + struct nfs_fattr *fattr, + struct nfs_fsinfo *fsinfo, + u32 *bmres) +{ + __nfs4_setup_getattr(cp, nfs4_mount_bitmap, + fattr, NULL, fsinfo, NULL, bmres); +} + +static void +nfs4_setup_statfs(struct nfs4_compound *cp, + struct nfs_fsstat *fsstat, + u32 *bmres) +{ + __nfs4_setup_getattr(cp, nfs4_statfs_bitmap, + NULL, fsstat, NULL, NULL, bmres); +} + +static void +nfs4_setup_fsinfo(struct nfs4_compound *cp, + struct nfs_fsinfo *fsinfo, + u32 *bmres) +{ + __nfs4_setup_getattr(cp, nfs4_fsinfo_bitmap, + NULL, NULL, fsinfo, NULL, bmres); +} + +static void +nfs4_setup_pathconf(struct nfs4_compound *cp, + struct nfs_pathconf *pathconf, + u32 *bmres) +{ + __nfs4_setup_getattr(cp, nfs4_pathconf_bitmap, + NULL, NULL, NULL, pathconf, bmres); +} + +static void +nfs4_setup_getfh(struct nfs4_compound *cp, struct nfs_fh *fhandle) +{ + struct nfs4_getfh *getfh = GET_OP(cp, getfh); + + getfh->gf_fhandle = fhandle; + + OPNUM(cp) = OP_GETFH; + cp->req_nops++; +} + +static void +nfs4_setup_link(struct nfs4_compound *cp, struct qstr *name, + struct nfs4_change_info *info) +{ + struct nfs4_link *link = GET_OP(cp, link); + + link->ln_namelen = name->len; + link->ln_name = name->name; + link->ln_cinfo = info; + + OPNUM(cp) = OP_LINK; + cp->req_nops++; +} + +static void +nfs4_setup_lookup(struct nfs4_compound *cp, struct qstr *q) +{ + struct nfs4_lookup *lookup = GET_OP(cp, lookup); + + lookup->lo_name = q; + + OPNUM(cp) = OP_LOOKUP; + cp->req_nops++; +} + +static void +nfs4_setup_putfh(struct nfs4_compound *cp, struct nfs_fh *fhandle) +{ + struct nfs4_putfh *putfh = GET_OP(cp, putfh); + + putfh->pf_fhandle = fhandle; + + OPNUM(cp) = OP_PUTFH; + cp->req_nops++; +} + +static void +nfs4_setup_putrootfh(struct nfs4_compound *cp) +{ + OPNUM(cp) = OP_PUTROOTFH; + cp->req_nops++; +} + +static void +nfs4_setup_open(struct nfs4_compound *cp, int flags, struct qstr *name, + struct iattr *sattr, char *stateid, struct nfs4_change_info *cinfo, + u32 *rflags) +{ + struct nfs4_open *open = GET_OP(cp, open); + + BUG_ON(cp->flags); + + open->op_share_access = flags & 3; + open->op_opentype = (flags & O_CREAT) ? NFS4_OPEN_CREATE : NFS4_OPEN_NOCREATE; + open->op_createmode = NFS4_CREATE_UNCHECKED; + open->op_attrs = sattr; + if (flags & O_EXCL) { + u32 *p = (u32 *) open->op_verifier; + p[0] = jiffies; + p[1] = current->pid; + open->op_createmode = NFS4_CREATE_EXCLUSIVE; + } + open->op_name = name; + open->op_stateid = stateid; + open->op_cinfo = cinfo; + open->op_rflags = rflags; + + OPNUM(cp) = OP_OPEN; + cp->req_nops++; + cp->renew_index = cp->req_nops; +} + +static void +nfs4_setup_open_confirm(struct nfs4_compound *cp, char *stateid) +{ + struct nfs4_open_confirm *open_confirm = GET_OP(cp, open_confirm); + + open_confirm->oc_stateid = stateid; + + OPNUM(cp) = OP_OPEN_CONFIRM; + cp->req_nops++; + cp->renew_index = cp->req_nops; +} + +static void +nfs4_setup_read(struct nfs4_compound *cp, u64 offset, u32 length, + struct page **pages, unsigned int pgbase, u32 *eofp, u32 *bytes_read) +{ + struct nfs4_read *read = GET_OP(cp, read); + + read->rd_offset = offset; + read->rd_length = length; + read->rd_pages = pages; + read->rd_pgbase = pgbase; + read->rd_eof = eofp; + read->rd_bytes_read = bytes_read; + + OPNUM(cp) = OP_READ; + cp->req_nops++; +} + +static void +nfs4_setup_readdir(struct nfs4_compound *cp, u64 cookie, u32 *verifier, + struct page **pages, unsigned int bufsize, struct dentry *dentry) +{ + u32 *start, *p; + struct nfs4_readdir *readdir = GET_OP(cp, readdir); + + BUG_ON(bufsize < 80); + readdir->rd_cookie = (cookie > 2) ? cookie : 0; + memcpy(readdir->rd_req_verifier, verifier, sizeof(nfs4_verifier)); + readdir->rd_count = bufsize; + readdir->rd_bmval[0] = FATTR4_WORD0_FILEID; + readdir->rd_bmval[1] = 0; + readdir->rd_pages = pages; + readdir->rd_pgbase = 0; + + OPNUM(cp) = OP_READDIR; + cp->req_nops++; + + if (cookie >= 2) + return; + + /* + * NFSv4 servers do not return entries for '.' and '..' + * Therefore, we fake these entries here. We let '.' + * have cookie 0 and '..' have cookie 1. Note that + * when talking to the server, we always send cookie 0 + * instead of 1 or 2. + */ + start = p = (u32 *)kmap(*pages); + + if (cookie == 0) { + *p++ = xdr_one; /* next */ + *p++ = xdr_zero; /* cookie, first word */ + *p++ = xdr_one; /* cookie, second word */ + *p++ = xdr_one; /* entry len */ + memcpy(p, ".\0\0\0", 4); /* entry */ + p++; + *p++ = xdr_one; /* bitmap length */ + *p++ = htonl(FATTR4_WORD0_FILEID); /* bitmap */ + *p++ = htonl(8); /* attribute buffer length */ + p = xdr_encode_hyper(p, NFS_FILEID(dentry->d_inode)); + } + + *p++ = xdr_one; /* next */ + *p++ = xdr_zero; /* cookie, first word */ + *p++ = xdr_two; /* cookie, second word */ + *p++ = xdr_two; /* entry len */ + memcpy(p, "..\0\0", 4); /* entry */ + p++; + *p++ = xdr_one; /* bitmap length */ + *p++ = htonl(FATTR4_WORD0_FILEID); /* bitmap */ + *p++ = htonl(8); /* attribute buffer length */ + p = xdr_encode_hyper(p, NFS_FILEID(dentry->d_parent->d_inode)); + + readdir->rd_pgbase = (char *)p - (char *)start; + readdir->rd_count -= readdir->rd_pgbase; + kunmap(*pages); +} + +static void +nfs4_setup_readlink(struct nfs4_compound *cp, int count, struct page **pages) +{ + struct nfs4_readlink *readlink = GET_OP(cp, readlink); + + readlink->rl_count = count; + readlink->rl_pages = pages; + + OPNUM(cp) = OP_READLINK; + cp->req_nops++; +} + +static void +nfs4_setup_remove(struct nfs4_compound *cp, struct qstr *name, struct nfs4_change_info *cinfo) +{ + struct nfs4_remove *remove = GET_OP(cp, remove); + + remove->rm_namelen = name->len; + remove->rm_name = name->name; + remove->rm_cinfo = cinfo; + + OPNUM(cp) = OP_REMOVE; + cp->req_nops++; +} + +static void +nfs4_setup_rename(struct nfs4_compound *cp, struct qstr *old, struct qstr *new, + struct nfs4_change_info *old_cinfo, struct nfs4_change_info *new_cinfo) +{ + struct nfs4_rename *rename = GET_OP(cp, rename); + + rename->rn_oldnamelen = old->len; + rename->rn_oldname = old->name; + rename->rn_newnamelen = new->len; + rename->rn_newname = new->name; + rename->rn_src_cinfo = old_cinfo; + rename->rn_dst_cinfo = new_cinfo; + + OPNUM(cp) = OP_RENAME; + cp->req_nops++; +} + +static void +nfs4_setup_renew(struct nfs4_compound *cp) +{ + OPNUM(cp) = OP_RENEW; + cp->req_nops++; + cp->renew_index = cp->req_nops; +} + +static void +nfs4_setup_restorefh(struct nfs4_compound *cp) +{ + OPNUM(cp) = OP_RESTOREFH; + cp->req_nops++; +} + +static void +nfs4_setup_savefh(struct nfs4_compound *cp) +{ + OPNUM(cp) = OP_SAVEFH; + cp->req_nops++; +} + +static void +nfs4_setup_setattr(struct nfs4_compound *cp, char *stateid, struct iattr *iap) +{ + struct nfs4_setattr *setattr = GET_OP(cp, setattr); + + setattr->st_stateid = stateid; + setattr->st_iap = iap; + + OPNUM(cp) = OP_SETATTR; + cp->req_nops++; +} + +static void +nfs4_setup_setclientid(struct nfs4_compound *cp, u32 program, unsigned short port) +{ + struct nfs4_setclientid *setclientid = GET_OP(cp, setclientid); + struct nfs_server *server = cp->server; + struct timeval tv; + u32 *p; + + do_gettimeofday(&tv); + p = (u32 *)setclientid->sc_verifier; + *p++ = tv.tv_sec; + *p++ = tv.tv_usec; + setclientid->sc_name = server->ip_addr; + sprintf(setclientid->sc_netid, "udp"); + sprintf(setclientid->sc_uaddr, "%s.%d.%d", server->ip_addr, port >> 8, port & 255); + setclientid->sc_prog = program; + setclientid->sc_cb_ident = 0; + + OPNUM(cp) = OP_SETCLIENTID; + cp->req_nops++; +} + +static void +nfs4_setup_setclientid_confirm(struct nfs4_compound *cp) +{ + OPNUM(cp) = OP_SETCLIENTID_CONFIRM; + cp->req_nops++; + cp->renew_index = cp->req_nops; +} + +static void +nfs4_setup_write(struct nfs4_compound *cp, u64 offset, u32 length, int stable, + struct page **pages, unsigned int pgbase, u32 *bytes_written, + struct nfs_writeverf *verf) +{ + struct nfs4_write *write = GET_OP(cp, write); + + write->wr_offset = offset; + write->wr_stable_how = stable; + write->wr_len = length; + write->wr_bytes_written = bytes_written; + write->wr_verf = verf; + + write->wr_pages = pages; + write->wr_pgbase = pgbase; + + OPNUM(cp) = OP_WRITE; + cp->req_nops++; +} + +static inline void +process_lease(struct nfs4_compound *cp) +{ + struct nfs_server *server; + + /* + * Generic lease processing: If this operation contains a + * lease-renewing operation, and it succeeded, update the RENEW time + * in the superblock. Instead of the current time, we use the time + * when the request was sent out. (All we know is that the lease was + * renewed sometime between then and now, and we have to assume the + * worst case.) + * + * Notes: + * (1) renewd doesn't acquire the spinlock when messing with + * server->last_renewal; this is OK since rpciod always runs + * under the BKL. + * (2) cp->timestamp was set at the end of XDR encode. + */ + if (!cp->renew_index) + return; + if (!cp->toplevel_status || cp->resp_nops > cp->renew_index) { + server = cp->server; + spin_lock(&renew_lock); + if (server->last_renewal < cp->timestamp) + server->last_renewal = cp->timestamp; + spin_unlock(&renew_lock); + } +} + +static int +nfs4_call_compound(struct nfs4_compound *cp, struct rpc_cred *cred, int flags) +{ + int status; + struct rpc_message msg = { + .rpc_proc = NFSPROC4_COMPOUND, + .rpc_argp = cp, + .rpc_resp = cp, + .rpc_cred = cred, + }; + + status = rpc_call_sync(cp->server->client, &msg, flags); + if (!status) + process_lease(cp); + + return status; +} + +static inline void +process_cinfo(struct nfs4_change_info *info, struct nfs_fattr *fattr) +{ + BUG_ON((fattr->valid & NFS_ATTR_FATTR) == 0); + BUG_ON((fattr->valid & NFS_ATTR_FATTR_V4) == 0); + + if (fattr->change_attr == info->after) { + fattr->pre_change_attr = info->before; + fattr->valid |= NFS_ATTR_PRE_CHANGE; + fattr->timestamp = jiffies; + } +} + +static int +do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *sattr, + struct nfs_fattr *fattr, struct nfs_fh *fhandle, u32 *seqid, char *stateid) +{ + struct nfs4_compound compound; + struct nfs4_op ops[7]; + struct nfs4_change_info dir_cinfo; + struct nfs_fattr dir_attr; + u32 dir_bmres[2]; + u32 bmres[2]; + u32 rflags; + int status; + + dir_attr.valid = 0; + fattr->valid = 0; + nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "open"); + nfs4_setup_putfh(&compound, NFS_FH(dir)); + nfs4_setup_savefh(&compound); + nfs4_setup_open(&compound, flags, name, sattr, stateid, &dir_cinfo, &rflags); + nfs4_setup_getattr(&compound, fattr, bmres); + nfs4_setup_getfh(&compound, fhandle); + nfs4_setup_restorefh(&compound); + nfs4_setup_getattr(&compound, &dir_attr, dir_bmres); + if ((status = nfs4_call_compound(&compound, NULL, 0))) + return status; + + process_cinfo(&dir_cinfo, &dir_attr); + nfs_refresh_inode(dir, &dir_attr); + if (!(rflags & NFS4_OPEN_RESULT_CONFIRM)) { + *seqid = 1; + return 0; + } + *seqid = 2; + + nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "open_confirm"); + nfs4_setup_putfh(&compound, fhandle); + nfs4_setup_open_confirm(&compound, stateid); + return nfs4_call_compound(&compound, NULL, 0); +} + +static int +do_setattr(struct nfs_server *server, struct nfs_fattr *fattr, + struct nfs_fh *fhandle, struct iattr *sattr, char *stateid) +{ + struct nfs4_compound compound; + struct nfs4_op ops[3]; + u32 bmres[2]; + + fattr->valid = 0; + nfs4_setup_compound(&compound, ops, server, "setattr"); + nfs4_setup_putfh(&compound, fhandle); + nfs4_setup_setattr(&compound, stateid, sattr); + nfs4_setup_getattr(&compound, fattr, bmres); + return nfs4_call_compound(&compound, NULL, 0); +} + +static int +do_close(struct nfs_server *server, struct nfs_fh *fhandle, u32 seqid, char *stateid) +{ + struct nfs4_compound compound; + struct nfs4_op ops[2]; + + nfs4_setup_compound(&compound, ops, server, "close"); + nfs4_setup_putfh(&compound, fhandle); + nfs4_setup_close(&compound, stateid, seqid); + return nfs4_call_compound(&compound, NULL, 0); +} + +static int +nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_fattr *fattr) +{ + struct nfs4_compound compound; + struct nfs4_op ops[4]; + struct nfs_fsinfo fsinfo; + u32 bmres[2]; + unsigned char * p; + struct qstr q; + int status; + + fattr->valid = 0; + + if (!(server->nfs4_state = nfs4_get_client())) + return -ENOMEM; + + /* + * SETCLIENTID. + * Until delegations are imported, we don't bother setting the program + * number and port to anything meaningful. + */ + nfs4_setup_compound(&compound, ops, server, "setclientid"); + nfs4_setup_setclientid(&compound, 0, 0); + if ((status = nfs4_call_compound(&compound, NULL, 0))) + goto out; + + /* + * SETCLIENTID_CONFIRM, plus root filehandle. + * We also get the lease time here. + */ + nfs4_setup_compound(&compound, ops, server, "setclientid_confirm"); + nfs4_setup_setclientid_confirm(&compound); + nfs4_setup_putrootfh(&compound); + nfs4_setup_getrootattr(&compound, fattr, &fsinfo, bmres); + nfs4_setup_getfh(&compound, fhandle); + if ((status = nfs4_call_compound(&compound, NULL, 0))) + goto out; + + /* + * Now that we have instantiated the clientid and determined + * the lease time, we can initialize the renew daemon for this + * server. + */ + server->lease_time = fsinfo.lease_time * HZ; + if ((status = nfs4_init_renewd(server))) + goto out; + + /* + * Now we do a seperate LOOKUP for each component of the mount path. + * The LOOKUPs are done seperately so that we can conveniently + * catch an ERR_WRONGSEC if it occurs along the way... + */ + p = server->mnt_path; + for (;;) { + while (*p == '/') + p++; + if (!*p) + break; + q.name = p; + while (*p && (*p != '/')) + p++; + q.len = p - q.name; + + nfs4_setup_compound(&compound, ops, server, "mount"); + nfs4_setup_putfh(&compound, fhandle); + nfs4_setup_lookup(&compound, &q); + nfs4_setup_getattr(&compound, fattr, bmres); + nfs4_setup_getfh(&compound, fhandle); + status = nfs4_call_compound(&compound, NULL, 0); + if (!status) + continue; + if (status == -ENOENT) { + printk(KERN_NOTICE "NFS: mount path %s does not exist!\n", server->mnt_path); + printk(KERN_NOTICE "NFS: suggestion: try mounting '/' instead.\n"); + } + break; + } + +out: + return status; +} + +static int +nfs4_proc_getattr(struct inode *inode, struct nfs_fattr *fattr) +{ + struct nfs4_compound compound; + struct nfs4_op ops[2]; + u32 bmres[2]; + + fattr->valid = 0; + + nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "getattr"); + nfs4_setup_putfh(&compound, NFS_FH(inode)); + nfs4_setup_getattr(&compound, fattr, bmres); + return nfs4_call_compound(&compound, NULL, 0); +} + +static int +nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, + struct iattr *sattr) +{ + struct inode * inode = dentry->d_inode; + int size_change = sattr->ia_valid & ATTR_SIZE; + struct nfs_fh throwaway_fh; + u32 seqid; + nfs4_stateid stateid; + int status; + + fattr->valid = 0; + + if (size_change) { + status = do_open(dentry->d_parent->d_inode, &dentry->d_name, + NFS4_SHARE_ACCESS_WRITE, NULL, fattr, + &throwaway_fh, &seqid, stateid); + if (status) + return status; + + /* + * Because OPEN is always done by name in nfsv4, it is + * possible that we opened a different file by the same + * name. We can recognize this race condition, but we + * can't do anything about it besides returning an error. + * + * XXX: Should we compare filehandles too, as in + * nfs_find_actor()? + */ + if (fattr->fileid != NFS_FILEID(inode)) { + printk(KERN_WARNING "nfs: raced in setattr, returning -EIO\n"); + do_close(NFS_SERVER(inode), NFS_FH(inode), seqid, stateid); + return -EIO; + } + } + else + memcpy(stateid, zero_stateid, sizeof(nfs4_stateid)); + + status = do_setattr(NFS_SERVER(inode), fattr, NFS_FH(inode), sattr, stateid); + if (size_change) + do_close(NFS_SERVER(inode), NFS_FH(inode), seqid, stateid); + return status; +} + +static int +nfs4_proc_lookup(struct inode *dir, struct qstr *name, + struct nfs_fh *fhandle, struct nfs_fattr *fattr) +{ + struct nfs4_compound compound; + struct nfs4_op ops[5]; + struct nfs_fattr dir_attr; + u32 dir_bmres[2]; + u32 bmres[2]; + int status; + + dir_attr.valid = 0; + fattr->valid = 0; + + dprintk("NFS call lookup %s\n", name->name); + nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "lookup"); + nfs4_setup_putfh(&compound, NFS_FH(dir)); + nfs4_setup_getattr(&compound, &dir_attr, dir_bmres); + nfs4_setup_lookup(&compound, name); + nfs4_setup_getattr(&compound, fattr, bmres); + nfs4_setup_getfh(&compound, fhandle); + status = nfs4_call_compound(&compound, NULL, 0); + dprintk("NFS reply lookup: %d\n", status); + + if (status >= 0) + status = nfs_refresh_inode(dir, &dir_attr); + return status; +} + +static int +nfs4_proc_access(struct inode *inode, struct rpc_cred *cred, int mode) +{ + struct nfs4_compound compound; + struct nfs4_op ops[3]; + struct nfs_fattr fattr; + u32 bmres[2]; + u32 req_access = 0, resp_supported, resp_access; + int status; + + fattr.valid = 0; + + /* + * Determine which access bits we want to ask for... + */ + if (mode & MAY_READ) + req_access |= NFS4_ACCESS_READ; + if (S_ISDIR(inode->i_mode)) { + if (mode & MAY_WRITE) + req_access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE; + if (mode & MAY_EXEC) + req_access |= NFS4_ACCESS_LOOKUP; + } + else { + if (mode & MAY_WRITE) + req_access |= NFS4_ACCESS_MODIFY | NFS4_ACCESS_EXTEND; + if (mode & MAY_EXEC) + req_access |= NFS4_ACCESS_EXECUTE; + } + + nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "access"); + nfs4_setup_putfh(&compound, NFS_FH(inode)); + nfs4_setup_getattr(&compound, &fattr, bmres); + nfs4_setup_access(&compound, req_access, &resp_supported, &resp_access); + status = nfs4_call_compound(&compound, cred, 0); + nfs_refresh_inode(inode, &fattr); + + if (!status) { + if (req_access != resp_supported) { + printk(KERN_NOTICE "NFS: server didn't support all access bits!\n"); + status = -ENOTSUPP; + } + else if (req_access != resp_access) + status = -EACCES; + } + return status; +} + +/* + * TODO: For the time being, we don't try to get any attributes + * along with any of the zero-copy operations READ, READDIR, + * READLINK, WRITE. + * + * In the case of the first three, we want to put the GETATTR + * after the read-type operation -- this is because it is hard + * to predict the length of a GETATTR response in v4, and thus + * align the READ data correctly. This means that the GETATTR + * may end up partially falling into the page cache, and we should + * shift it into the 'tail' of the xdr_buf before processing. + * To do this efficiently, we need to know the total length + * of data received, which doesn't seem to be available outside + * of the RPC layer. + * + * In the case of WRITE, we also want to put the GETATTR after + * the operation -- in this case because we want to make sure + * we get the post-operation mtime and size. This means that + * we can't use xdr_encode_pages() as written: we need a variant + * of it which would leave room in the 'tail' iovec. + * + * Both of these changes to the XDR layer would in fact be quite + * minor, but I decided to leave them for a subsequent patch. + */ +static int +nfs4_proc_readlink(struct inode *inode, struct page *page) +{ + struct nfs4_compound compound; + struct nfs4_op ops[2]; + + nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "readlink"); + nfs4_setup_putfh(&compound, NFS_FH(inode)); + nfs4_setup_readlink(&compound, PAGE_CACHE_SIZE, &page); + return nfs4_call_compound(&compound, NULL, 0); +} + +static int +nfs4_proc_read(struct inode *inode, struct rpc_cred *cred, + struct nfs_fattr *fattr, int flags, + unsigned int base, unsigned int count, + struct page *page, int *eofp) +{ + u64 offset = page_offset(page) + base; + struct nfs4_compound compound; + struct nfs4_op ops[2]; + u32 bytes_read; + int status; + + fattr->valid = 0; + nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "read [sync]"); + nfs4_setup_putfh(&compound, NFS_FH(inode)); + nfs4_setup_read(&compound, offset, count, &page, base, eofp, &bytes_read); + status = nfs4_call_compound(&compound, cred, 0); + + if (status >= 0) + status = bytes_read; + return status; +} + +static int +nfs4_proc_write(struct inode *inode, struct rpc_cred *cred, + struct nfs_fattr *fattr, int flags, + unsigned int base, unsigned int count, + struct page *page, struct nfs_writeverf *verf) +{ + u64 offset = page_offset(page) + base; + struct nfs4_compound compound; + struct nfs4_op ops[2]; + u32 bytes_written; + int stable = (flags & NFS_RW_SYNC) ? NFS_FILE_SYNC : NFS_UNSTABLE; + int rpcflags = (flags & NFS_RW_SWAP) ? NFS_RPC_SWAPFLAGS : 0; + int status; + + fattr->valid = 0; + nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "write [sync]"); + nfs4_setup_putfh(&compound, NFS_FH(inode)); + nfs4_setup_write(&compound, offset, count, stable, &page, base, &bytes_written, verf); + status = nfs4_call_compound(&compound, cred, rpcflags); + + if (status >= 0) + status = bytes_written; + return status; +} + +static int +nfs4_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr, + int flags, struct nfs_fh *fhandle, struct nfs_fattr *fattr) +{ + int oflags; + u32 seqid; + nfs4_stateid stateid; + int status; + + oflags = NFS4_SHARE_ACCESS_READ | O_CREAT | (flags & O_EXCL); + status = do_open(dir, name, oflags, sattr, fattr, fhandle, &seqid, stateid); + if (!status) { + if (flags & O_EXCL) + status = do_setattr(NFS_SERVER(dir), fattr, fhandle, sattr, stateid); + do_close(NFS_SERVER(dir), fhandle, seqid, stateid); + } + return status; +} + +static int +nfs4_proc_remove(struct inode *dir, struct qstr *name) +{ + struct nfs4_compound compound; + struct nfs4_op ops[3]; + struct nfs4_change_info dir_cinfo; + struct nfs_fattr dir_attr; + u32 dir_bmres[2]; + int status; + + dir_attr.valid = 0; + nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "remove"); + nfs4_setup_putfh(&compound, NFS_FH(dir)); + nfs4_setup_remove(&compound, name, &dir_cinfo); + nfs4_setup_getattr(&compound, &dir_attr, dir_bmres); + status = nfs4_call_compound(&compound, NULL, 0); + + if (!status) { + process_cinfo(&dir_cinfo, &dir_attr); + nfs_refresh_inode(dir, &dir_attr); + } + return status; +} + +struct unlink_desc { + struct nfs4_compound compound; + struct nfs4_op ops[3]; + struct nfs4_change_info cinfo; + struct nfs_fattr attrs; +}; + +static int +nfs4_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name) +{ + struct unlink_desc * up; + struct nfs4_compound * cp; + u32 bmres[2]; + + up = (struct unlink_desc *) kmalloc(sizeof(*up), GFP_KERNEL); + if (!up) + return -ENOMEM; + cp = &up->compound; + + nfs4_setup_compound(cp, up->ops, NFS_SERVER(dir->d_inode), "unlink_setup"); + nfs4_setup_putfh(cp, NFS_FH(dir->d_inode)); + nfs4_setup_remove(cp, name, &up->cinfo); + nfs4_setup_getattr(cp, &up->attrs, bmres); + + msg->rpc_proc = NFSPROC4_COMPOUND; + msg->rpc_argp = cp; + msg->rpc_resp = cp; + return 0; +} + +static int +nfs4_proc_unlink_done(struct dentry *dir, struct rpc_task *task) +{ + struct rpc_message *msg = &task->tk_msg; + struct unlink_desc *up; + + if (msg->rpc_argp) { + up = (struct unlink_desc *) msg->rpc_argp; + process_lease(&up->compound); + process_cinfo(&up->cinfo, &up->attrs); + nfs_refresh_inode(dir->d_inode, &up->attrs); + kfree(up); + msg->rpc_argp = NULL; + } + return 0; +} + +static int +nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name, + struct inode *new_dir, struct qstr *new_name) +{ + struct nfs4_compound compound; + struct nfs4_op ops[7]; + struct nfs4_change_info old_cinfo, new_cinfo; + struct nfs_fattr old_dir_attr, new_dir_attr; + u32 old_dir_bmres[2], new_dir_bmres[2]; + int status; + + old_dir_attr.valid = 0; + new_dir_attr.valid = 0; + + nfs4_setup_compound(&compound, ops, NFS_SERVER(old_dir), "rename"); + nfs4_setup_putfh(&compound, NFS_FH(old_dir)); + nfs4_setup_savefh(&compound); + nfs4_setup_putfh(&compound, NFS_FH(new_dir)); + nfs4_setup_rename(&compound, old_name, new_name, &old_cinfo, &new_cinfo); + nfs4_setup_getattr(&compound, &new_dir_attr, new_dir_bmres); + nfs4_setup_restorefh(&compound); + nfs4_setup_getattr(&compound, &old_dir_attr, old_dir_bmres); + status = nfs4_call_compound(&compound, NULL, 0); + + if (!status) { + process_cinfo(&old_cinfo, &old_dir_attr); + process_cinfo(&new_cinfo, &new_dir_attr); + nfs_refresh_inode(old_dir, &old_dir_attr); + nfs_refresh_inode(new_dir, &new_dir_attr); + } + return status; +} + +static int +nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name) +{ + struct nfs4_compound compound; + struct nfs4_op ops[7]; + struct nfs4_change_info dir_cinfo; + struct nfs_fattr dir_attr, fattr; + u32 dir_bmres[2], bmres[2]; + int status; + + dir_attr.valid = 0; + fattr.valid = 0; + + nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "link"); + nfs4_setup_putfh(&compound, NFS_FH(inode)); + nfs4_setup_savefh(&compound); + nfs4_setup_putfh(&compound, NFS_FH(dir)); + nfs4_setup_link(&compound, name, &dir_cinfo); + nfs4_setup_getattr(&compound, &dir_attr, dir_bmres); + nfs4_setup_restorefh(&compound); + nfs4_setup_getattr(&compound, &fattr, bmres); + status = nfs4_call_compound(&compound, NULL, 0); + + if (!status) { + process_cinfo(&dir_cinfo, &dir_attr); + nfs_refresh_inode(dir, &dir_attr); + nfs_refresh_inode(inode, &fattr); + } + return status; +} + +static int +nfs4_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path, + struct iattr *sattr, struct nfs_fh *fhandle, + struct nfs_fattr *fattr) +{ + struct nfs4_compound compound; + struct nfs4_op ops[7]; + struct nfs_fattr dir_attr; + u32 dir_bmres[2], bmres[2]; + struct nfs4_change_info dir_cinfo; + int status; + + dir_attr.valid = 0; + fattr->valid = 0; + + nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "symlink"); + nfs4_setup_putfh(&compound, NFS_FH(dir)); + nfs4_setup_savefh(&compound); + nfs4_setup_create_symlink(&compound, name, path, sattr, &dir_cinfo); + nfs4_setup_getattr(&compound, fattr, bmres); + nfs4_setup_getfh(&compound, fhandle); + nfs4_setup_restorefh(&compound); + nfs4_setup_getattr(&compound, &dir_attr, dir_bmres); + status = nfs4_call_compound(&compound, NULL, 0); + + if (!status) { + process_cinfo(&dir_cinfo, &dir_attr); + nfs_refresh_inode(dir, &dir_attr); + } + return status; +} + +static int +nfs4_proc_mkdir(struct inode *dir, struct qstr *name, struct iattr *sattr, + struct nfs_fh *fhandle, struct nfs_fattr *fattr) +{ + struct nfs4_compound compound; + struct nfs4_op ops[7]; + struct nfs_fattr dir_attr; + u32 dir_bmres[2], bmres[2]; + struct nfs4_change_info dir_cinfo; + int status; + + dir_attr.valid = 0; + fattr->valid = 0; + + nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "mkdir"); + nfs4_setup_putfh(&compound, NFS_FH(dir)); + nfs4_setup_savefh(&compound); + nfs4_setup_create_dir(&compound, name, sattr, &dir_cinfo); + nfs4_setup_getattr(&compound, fattr, bmres); + nfs4_setup_getfh(&compound, fhandle); + nfs4_setup_restorefh(&compound); + nfs4_setup_getattr(&compound, &dir_attr, dir_bmres); + status = nfs4_call_compound(&compound, NULL, 0); + + if (!status) { + process_cinfo(&dir_cinfo, &dir_attr); + nfs_refresh_inode(dir, &dir_attr); + } + return status; +} + +static int +nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, + u64 cookie, struct page *page, unsigned int count, int plus) +{ + struct inode *dir = dentry->d_inode; + struct nfs4_compound compound; + struct nfs4_op ops[2]; + int status; + + lock_kernel(); + + nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "readdir"); + nfs4_setup_putfh(&compound, NFS_FH(dir)); + nfs4_setup_readdir(&compound, cookie, NFS_COOKIEVERF(dir), &page, count, dentry); + status = nfs4_call_compound(&compound, cred, 0); + + unlock_kernel(); + return status; +} + +static int +nfs4_proc_mknod(struct inode *dir, struct qstr *name, struct iattr *sattr, + dev_t rdev, struct nfs_fh *fh, struct nfs_fattr *fattr) +{ + struct nfs4_compound compound; + struct nfs4_op ops[7]; + struct nfs_fattr dir_attr; + u32 dir_bmres[2], bmres[2]; + struct nfs4_change_info dir_cinfo; + int status; + + dir_attr.valid = 0; + fattr->valid = 0; + + nfs4_setup_compound(&compound, ops, NFS_SERVER(dir), "mknod"); + nfs4_setup_putfh(&compound, NFS_FH(dir)); + nfs4_setup_savefh(&compound); + nfs4_setup_create_special(&compound, name, rdev,sattr, &dir_cinfo); + nfs4_setup_getattr(&compound, fattr, bmres); + nfs4_setup_getfh(&compound, fh); + nfs4_setup_restorefh(&compound); + nfs4_setup_getattr(&compound, &dir_attr, dir_bmres); + status = nfs4_call_compound(&compound, NULL, 0); + + if (!status) { + process_cinfo(&dir_cinfo, &dir_attr); + nfs_refresh_inode(dir, &dir_attr); + } + return status; +} + +static int +nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_fsstat *fsstat) +{ + struct nfs4_compound compound; + struct nfs4_op ops[2]; + u32 bmres[2]; + + memset(fsstat, 0, sizeof(*fsstat)); + nfs4_setup_compound(&compound, ops, server, "statfs"); + nfs4_setup_putfh(&compound, fhandle); + nfs4_setup_statfs(&compound, fsstat, bmres); + return nfs4_call_compound(&compound, NULL, 0); +} + +static int +nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_fsinfo *fsinfo) +{ + struct nfs4_compound compound; + struct nfs4_op ops[2]; + u32 bmres[2]; + + memset(fsinfo, 0, sizeof(*fsinfo)); + nfs4_setup_compound(&compound, ops, server, "statfs"); + nfs4_setup_putfh(&compound, fhandle); + nfs4_setup_fsinfo(&compound, fsinfo, bmres); + return nfs4_call_compound(&compound, NULL, 0); +} + +static int +nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_pathconf *pathconf) +{ + struct nfs4_compound compound; + struct nfs4_op ops[2]; + u32 bmres[2]; + + memset(pathconf, 0, sizeof(*pathconf)); + nfs4_setup_compound(&compound, ops, server, "statfs"); + nfs4_setup_putfh(&compound, fhandle); + nfs4_setup_pathconf(&compound, pathconf, bmres); + return nfs4_call_compound(&compound, NULL, 0); +} + +static void +nfs4_read_done(struct rpc_task *task) +{ + struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata; + + process_lease(&data->u.v4.compound); + nfs_readpage_result(task, data->u.v4.res_count, data->u.v4.res_eof); +} + +static void +nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count) +{ + struct rpc_task *task = &data->task; + struct nfs4_compound *cp = &data->u.v4.compound; + struct rpc_message msg = { + .rpc_proc = NFSPROC4_COMPOUND, + .rpc_argp = cp, + .rpc_resp = cp, + .rpc_cred = data->cred, + }; + struct inode *inode = data->inode; + struct nfs_page *req = nfs_list_entry(data->pages.next); + int flags; + + nfs4_setup_compound(cp, data->u.v4.ops, NFS_SERVER(inode), "read [async]"); + nfs4_setup_putfh(cp, NFS_FH(inode)); + nfs4_setup_read(cp, req_offset(req) + req->wb_offset, + count, data->pagevec, req->wb_offset, + &data->u.v4.res_eof, + &data->u.v4.res_count); + + /* N.B. Do we need to test? Never called for swapfile inode */ + flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0); + + /* Finalize the task. */ + rpc_init_task(task, NFS_CLIENT(inode), nfs4_read_done, flags); + task->tk_calldata = data; + /* Release requests */ + task->tk_release = nfs_readdata_release; + + rpc_call_setup(task, &msg, 0); +} + +static void +nfs4_write_done(struct rpc_task *task) +{ + struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; + + process_lease(&data->u.v4.compound); + nfs_writeback_done(task, data->u.v4.arg_stable, + data->u.v4.arg_count, data->u.v4.res_count); +} + +static void +nfs4_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how) +{ + struct rpc_task *task = &data->task; + struct nfs4_compound *cp = &data->u.v4.compound; + struct rpc_message msg = { + .rpc_proc = NFSPROC4_COMPOUND, + .rpc_argp = cp, + .rpc_resp = cp, + .rpc_cred = data->cred, + }; + struct inode *inode = data->inode; + struct nfs_page *req = nfs_list_entry(data->pages.next); + int stable; + int flags; + + if (how & FLUSH_STABLE) { + if (!NFS_I(inode)->ncommit) + stable = NFS_FILE_SYNC; + else + stable = NFS_DATA_SYNC; + } else + stable = NFS_UNSTABLE; + + nfs4_setup_compound(cp, data->u.v4.ops, NFS_SERVER(inode), "write [async]"); + nfs4_setup_putfh(cp, NFS_FH(inode)); + nfs4_setup_write(cp, req_offset(req) + req->wb_offset, + count, stable, data->pagevec, req->wb_offset, + &data->u.v4.res_count, &data->verf); + + /* Set the initial flags for the task. */ + flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; + + /* Finalize the task. */ + rpc_init_task(task, NFS_CLIENT(inode), nfs4_write_done, flags); + task->tk_calldata = data; + /* Release requests */ + task->tk_release = nfs_writedata_release; + + rpc_call_setup(task, &msg, 0); +} + +static void +nfs4_commit_done(struct rpc_task *task) +{ + struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; + + process_lease(&data->u.v4.compound); + nfs_commit_done(task); +} + +static void +nfs4_proc_commit_setup(struct nfs_write_data *data, u64 start, u32 len, int how) +{ + struct rpc_task *task = &data->task; + struct nfs4_compound *cp = &data->u.v4.compound; + struct rpc_message msg = { + .rpc_proc = NFSPROC4_COMPOUND, + .rpc_argp = cp, + .rpc_resp = cp, + .rpc_cred = data->cred, + }; + struct inode *inode = data->inode; + int flags; + + nfs4_setup_compound(cp, data->u.v4.ops, NFS_SERVER(inode), "commit [async]"); + nfs4_setup_putfh(cp, NFS_FH(inode)); + nfs4_setup_commit(cp, start, len, &data->verf); + + /* Set the initial flags for the task. */ + flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; + + /* Finalize the task. */ + rpc_init_task(task, NFS_CLIENT(inode), nfs4_commit_done, flags); + task->tk_calldata = data; + /* Release requests */ + task->tk_release = nfs_writedata_release; + + rpc_call_setup(task, &msg, 0); +} + +/* + * nfs4_proc_renew(): This is not one of the nfs_rpc_ops; it is a special + * standalone procedure for queueing an asynchronous RENEW. + */ +struct renew_desc { + struct rpc_task task; + struct nfs4_compound compound; + struct nfs4_op ops[1]; +}; + +static void +renew_done(struct rpc_task *task) +{ + struct nfs4_compound *cp = (struct nfs4_compound *) task->tk_msg.rpc_argp; + process_lease(cp); +} + +static void +renew_release(struct rpc_task *task) +{ + kfree(task->tk_calldata); + task->tk_calldata = NULL; +} + +int +nfs4_proc_renew(struct nfs_server *server) +{ + struct renew_desc *rp; + struct rpc_task *task; + struct nfs4_compound *cp; + struct rpc_message msg; + + rp = (struct renew_desc *) kmalloc(sizeof(*rp), GFP_KERNEL); + if (!rp) + return -ENOMEM; + cp = &rp->compound; + task = &rp->task; + + nfs4_setup_compound(cp, rp->ops, server, "renew"); + nfs4_setup_renew(cp); + + msg.rpc_proc = NFSPROC4_COMPOUND; + msg.rpc_argp = cp; + msg.rpc_resp = cp; + msg.rpc_cred = NULL; + rpc_init_task(task, server->client, renew_done, RPC_TASK_ASYNC); + rpc_call_setup(task, &msg, 0); + task->tk_calldata = rp; + task->tk_release = renew_release; + + return rpc_execute(task); +} + +struct nfs_rpc_ops nfs_v4_clientops = { + .version = 4, /* protocol version */ + .getroot = nfs4_proc_get_root, + .getattr = nfs4_proc_getattr, + .setattr = nfs4_proc_setattr, + .lookup = nfs4_proc_lookup, + .access = nfs4_proc_access, + .readlink = nfs4_proc_readlink, + .read = nfs4_proc_read, + .write = nfs4_proc_write, + .commit = NULL, + .create = nfs4_proc_create, + .remove = nfs4_proc_remove, + .unlink_setup = nfs4_proc_unlink_setup, + .unlink_done = nfs4_proc_unlink_done, + .rename = nfs4_proc_rename, + .link = nfs4_proc_link, + .symlink = nfs4_proc_symlink, + .mkdir = nfs4_proc_mkdir, + .rmdir = nfs4_proc_remove, + .readdir = nfs4_proc_readdir, + .mknod = nfs4_proc_mknod, + .statfs = nfs4_proc_statfs, + .fsinfo = nfs4_proc_fsinfo, + .pathconf = nfs4_proc_pathconf, + .decode_dirent = nfs4_decode_dirent, + .read_setup = nfs4_proc_read_setup, + .write_setup = nfs4_proc_write_setup, + .commit_setup = nfs4_proc_commit_setup, +}; + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/fs/nfs/nfs4renewd.c b/fs/nfs/nfs4renewd.c new file mode 100644 index 000000000000..4ba871885dbc --- /dev/null +++ b/fs/nfs/nfs4renewd.c @@ -0,0 +1,110 @@ +/* + * fs/nfs/nfs4renewd.c + * + * Copyright (c) 2002 The Regents of the University of Michigan. + * All rights reserved. + * + * Kendrick Smith + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' 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 THE REGENTS OR CONTRIBUTORS 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. + * + * Implementation of the NFSv4 "renew daemon", which wakes up periodically to + * send a RENEW, to keep state alive on the server. The daemon is implemented + * as an rpc_task, not a real kernel thread, so it always runs in rpciod's + * context. There is one renewd per nfs_server. + * + * TODO: If the send queue gets backlogged (e.g., if the server goes down), + * we will keep filling the queue with periodic RENEW requests. We need a + * mechanism for ensuring that if renewd successfully sends off a request, + * then it only wakes up when the request is finished. Maybe use the + * child task framework of the RPC layer? + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static RPC_WAITQ(nfs4_renewd_queue, "nfs4_renewd_queue"); + +static void +renewd(struct rpc_task *task) +{ + struct nfs_server *server = (struct nfs_server *)task->tk_calldata; + unsigned long lease = server->lease_time; + unsigned long last = server->last_renewal; + unsigned long timeout; + + if (!server->nfs4_state) + timeout = (2 * lease) / 3; + else if (jiffies < last + lease/3) + timeout = (2 * lease) / 3 + last - jiffies; + else { + /* Queue an asynchronous RENEW. */ + nfs4_proc_renew(server); + timeout = (2 * lease) / 3; + } + + if (timeout < 5 * HZ) /* safeguard */ + timeout = 5 * HZ; + task->tk_timeout = timeout; + task->tk_action = renewd; + task->tk_exit = NULL; + rpc_sleep_on(&nfs4_renewd_queue, task, NULL, NULL); + return; +} + +int +nfs4_init_renewd(struct nfs_server *server) +{ + struct rpc_task *task; + int status; + + lock_kernel(); + status = -ENOMEM; + task = rpc_new_task(server->client, NULL, RPC_TASK_ASYNC); + if (!task) + goto out; + task->tk_calldata = server; + task->tk_action = renewd; + status = rpc_execute(task); + +out: + unlock_kernel(); + return status; +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c new file mode 100644 index 000000000000..ecbc54fb1048 --- /dev/null +++ b/fs/nfs/nfs4state.c @@ -0,0 +1,81 @@ +/* + * fs/nfs/nfs4state.c + * + * Client-side XDR for NFSv4. + * + * Copyright (c) 2002 The Regents of the University of Michigan. + * All rights reserved. + * + * Kendrick Smith + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' 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 THE REGENTS OR CONTRIBUTORS 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. + * + * Implementation of the NFSv4 state model. For the time being, + * this is minimal, but will be made much more complex in a + * subsequent patch. + */ + +#include +#include +#include + +/* + * nfs4_get_client(): returns an empty client structure + * nfs4_put_client(): drops reference to client structure + * + * Since these are allocated/deallocated very rarely, we don't + * bother putting them in a slab cache... + */ +struct nfs4_client * +nfs4_get_client(void) +{ + struct nfs4_client *clp; + + if ((clp = kmalloc(sizeof(*clp), GFP_KERNEL))) { + atomic_set(&clp->cl_count, 1); + clp->cl_clientid = 0; + INIT_LIST_HEAD(&clp->cl_lockowners); + } + return clp; +} + +void +nfs4_put_client(struct nfs4_client *clp) +{ + BUG_ON(!clp); + BUG_ON(!atomic_read(&clp->cl_count)); + + if (atomic_dec_and_test(&clp->cl_count)) { + BUG_ON(!list_empty(&clp->cl_lockowners)); + kfree(clp); + } +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c new file mode 100644 index 000000000000..edbf0e2a02d7 --- /dev/null +++ b/fs/nfs/nfs4xdr.c @@ -0,0 +1,1777 @@ +/* + * fs/nfs/nfs4xdr.c + * + * Client-side XDR for NFSv4. + * + * Copyright (c) 2002 The Regents of the University of Michigan. + * All rights reserved. + * + * Kendrick Smith + * Andy Adamson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' 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 THE REGENTS OR CONTRIBUTORS 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Emperically, it seems that the NFS client gets confused if + * cookies larger than this are returned -- presumably a + * signedness issue? + */ +#define COOKIE_MAX 0x7fffffff + +#define NFS4_CLIENTID(server) ((server)->nfs4_state->cl_clientid) + +#define NFSDBG_FACILITY NFSDBG_XDR + +/* Mapping from NFS error code to "errno" error code. */ +#define errno_NFSERR_IO EIO + +extern int nfs_stat_to_errno(int); + +#define NFS4_enc_void_sz 0 +#define NFS4_dec_void_sz 0 +#define NFS4_enc_compound_sz 1024 /* XXX: large enough? */ +#define NFS4_dec_compound_sz 1024 /* XXX: large enough? */ + +static struct { + unsigned int mode; + unsigned int nfs2type; +} nfs_type2fmt[] = { + { 0, NFNON }, + { S_IFREG, NFREG }, + { S_IFDIR, NFDIR }, + { S_IFBLK, NFBLK }, + { S_IFCHR, NFCHR }, + { S_IFLNK, NFLNK }, + { S_IFSOCK, NFSOCK }, + { S_IFIFO, NFFIFO }, + { 0, NFNON }, + { 0, NFNON }, +}; + +/* + * START OF "GENERIC" ENCODE ROUTINES. + * These may look a little ugly since they are imported from a "generic" + * set of XDR encode/decode routines which are intended to be shared by + * all of our NFSv4 implementations (OpenBSD, MacOS X...). + * + * If the pain of reading these is too great, it should be a straightforward + * task to translate them into Linux-specific versions which are more + * consistent with the style used in NFSv2/v3... + */ +#define ENCODE_HEAD \ + u32 *p; +#define ENCODE_TAIL \ + return 0 + +#define WRITE32(n) *p++ = htonl(n) +#define WRITE64(n) do { \ + *p++ = htonl((u32)((n) >> 32)); \ + *p++ = htonl((u32)(n)); \ +} while (0) +#define WRITEMEM(ptr,nbytes) do { \ + p = xdr_writemem(p, ptr, nbytes); \ +} while (0) + +#define RESERVE_SPACE(nbytes) do { BUG_ON(cp->p + XDR_QUADLEN(nbytes) > cp->end); p = cp->p; } while (0) +#define ADJUST_ARGS() cp->p = p + +static inline +u32 *xdr_writemem(u32 *p, const void *ptr, int nbytes) +{ + int tmp = XDR_QUADLEN(nbytes); + if (!tmp) + return p; + p[tmp-1] = 0; + memcpy(p, ptr, nbytes); + return p + tmp; +} + +/* + * FIXME: The following dummy entries will be replaced once the userland + * upcall gets in... + */ +static int +encode_uid(char *p, uid_t uid) +{ + strcpy(p, "nobody"); + return 6; +} + +/* + * FIXME: The following dummy entries will be replaced once the userland + * upcall gets in... + */ +static int +encode_gid(char *p, gid_t gid) +{ + strcpy(p, "nobody"); + return 6; +} + +static int +encode_attrs(struct nfs4_compound *cp, struct iattr *iap) +{ + char owner_name[256]; + char owner_group[256]; + int owner_namelen = 0; + int owner_grouplen = 0; + u32 *q; + int len; + u32 bmval0 = 0; + u32 bmval1 = 0; + int status; + ENCODE_HEAD; + + /* + * We reserve enough space to write the entire attribute buffer at once. + * In the worst-case, this would be + * 12(bitmap) + 4(attrlen) + 8(size) + 4(mode) + 4(atime) + 4(mtime) + * = 36 bytes, plus any contribution from variable-length fields + * such as owner/group/acl's. + */ + len = 36; + + /* Sigh */ + if (iap->ia_valid & ATTR_UID) { + status = owner_namelen = encode_uid(owner_name, iap->ia_uid); + if (status < 0) { + printk(KERN_WARNING "nfs: couldn't resolve uid %d to string\n", + iap->ia_uid); + goto out; + } + len += XDR_QUADLEN(owner_namelen); + } + if (iap->ia_valid & ATTR_GID) { + status = owner_grouplen = encode_gid(owner_group, iap->ia_gid); + if (status < 0) { + printk(KERN_WARNING "nfs4: couldn't resolve gid %d to string\n", + iap->ia_gid); + goto out; + } + len += XDR_QUADLEN(owner_grouplen); + } + RESERVE_SPACE(len); + + /* + * We write the bitmap length now, but leave the bitmap and the attribute + * buffer length to be backfilled at the end of this routine. + */ + WRITE32(2); + q = p; + p += 3; + + if (iap->ia_valid & ATTR_SIZE) { + bmval0 |= FATTR4_WORD0_SIZE; + WRITE64(iap->ia_size); + } + if (iap->ia_valid & ATTR_MODE) { + bmval1 |= FATTR4_WORD1_MODE; + WRITE32(iap->ia_mode); + } + if (iap->ia_valid & ATTR_UID) { + bmval1 |= FATTR4_WORD1_OWNER; + WRITE32(owner_namelen); + WRITEMEM(owner_name, owner_namelen); + p += owner_namelen; + } + if (iap->ia_valid & ATTR_GID) { + bmval1 |= FATTR4_WORD1_OWNER_GROUP; + WRITE32(owner_grouplen); + WRITEMEM(owner_group, owner_grouplen); + p += owner_namelen; + } + if (iap->ia_valid & ATTR_ATIME_SET) { + bmval1 |= FATTR4_WORD1_TIME_ACCESS_SET; + WRITE32(NFS4_SET_TO_CLIENT_TIME); + WRITE32(0); + WRITE32(iap->ia_mtime); + WRITE32(0); + } + else if (iap->ia_valid & ATTR_ATIME) { + bmval1 |= FATTR4_WORD1_TIME_ACCESS_SET; + WRITE32(NFS4_SET_TO_SERVER_TIME); + } + if (iap->ia_valid & ATTR_MTIME_SET) { + bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET; + WRITE32(NFS4_SET_TO_CLIENT_TIME); + WRITE32(0); + WRITE32(iap->ia_mtime); + WRITE32(0); + } + else if (iap->ia_valid & ATTR_MTIME) { + bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET; + WRITE32(NFS4_SET_TO_SERVER_TIME); + } + + ADJUST_ARGS(); + + /* + * Now we backfill the bitmap and the attribute buffer length. + */ + len = (char *)p - (char *)q - 12; + *q++ = htonl(bmval0); + *q++ = htonl(bmval1); + *q++ = htonl(len); + + status = 0; +out: + return status; +} + +static int +encode_access(struct nfs4_compound *cp, struct nfs4_access *access) +{ + ENCODE_HEAD; + + RESERVE_SPACE(8); + WRITE32(OP_ACCESS); + WRITE32(access->ac_req_access); + ADJUST_ARGS(); + + ENCODE_TAIL; +} + +static int +encode_close(struct nfs4_compound *cp, struct nfs4_close *close) +{ + ENCODE_HEAD; + + RESERVE_SPACE(20); + WRITE32(OP_CLOSE); + WRITE32(close->cl_seqid); + WRITEMEM(close->cl_stateid, sizeof(nfs4_stateid)); + ADJUST_ARGS(); + + ENCODE_TAIL; +} + +static int +encode_commit(struct nfs4_compound *cp, struct nfs4_commit *commit) +{ + ENCODE_HEAD; + + RESERVE_SPACE(16); + WRITE32(OP_COMMIT); + WRITE64(commit->co_start); + WRITE32(commit->co_len); + ADJUST_ARGS(); + + ENCODE_TAIL; +} + +static int +encode_create(struct nfs4_compound *cp, struct nfs4_create *create) +{ + ENCODE_HEAD; + + RESERVE_SPACE(8); + WRITE32(OP_CREATE); + WRITE32(create->cr_ftype); + ADJUST_ARGS(); + + switch (create->cr_ftype) { + case NF4LNK: + RESERVE_SPACE(4 + create->cr_textlen); + WRITE32(create->cr_textlen); + WRITEMEM(create->cr_text, create->cr_textlen); + ADJUST_ARGS(); + break; + + case NF4BLK: case NF4CHR: + RESERVE_SPACE(8); + WRITE32(create->cr_specdata1); + WRITE32(create->cr_specdata2); + ADJUST_ARGS(); + break; + + default: + break; + } + + RESERVE_SPACE(4 + create->cr_namelen); + WRITE32(create->cr_namelen); + WRITEMEM(create->cr_name, create->cr_namelen); + ADJUST_ARGS(); + + return encode_attrs(cp, create->cr_attrs); +} + +static int +encode_getattr(struct nfs4_compound *cp, struct nfs4_getattr *getattr) +{ + ENCODE_HEAD; + + RESERVE_SPACE(16); + WRITE32(OP_GETATTR); + WRITE32(2); + WRITE32(getattr->gt_bmval[0]); + WRITE32(getattr->gt_bmval[1]); + ADJUST_ARGS(); + + ENCODE_TAIL; +} + +static int +encode_getfh(struct nfs4_compound *cp) +{ + ENCODE_HEAD; + + RESERVE_SPACE(4); + WRITE32(OP_GETFH); + ADJUST_ARGS(); + + ENCODE_TAIL; +} + +static int +encode_link(struct nfs4_compound *cp, struct nfs4_link *link) +{ + ENCODE_HEAD; + + RESERVE_SPACE(8 + link->ln_namelen); + WRITE32(OP_LINK); + WRITE32(link->ln_namelen); + WRITEMEM(link->ln_name, link->ln_namelen); + ADJUST_ARGS(); + + ENCODE_TAIL; +} + +static int +encode_lookup(struct nfs4_compound *cp, struct nfs4_lookup *lookup) +{ + int len = lookup->lo_name->len; + ENCODE_HEAD; + + RESERVE_SPACE(8 + len); + WRITE32(OP_LOOKUP); + WRITE32(len); + WRITEMEM(lookup->lo_name->name, len); + ADJUST_ARGS(); + + ENCODE_TAIL; +} + +static int +encode_open(struct nfs4_compound *cp, struct nfs4_open *open) +{ + static int global_id = 0; + int id = global_id++; + int status; + ENCODE_HEAD; + + /* seqid, share_access, share_deny, clientid, ownerlen, owner, opentype */ + RESERVE_SPACE(52); + WRITE32(OP_OPEN); + WRITE32(0); /* seqid */ + WRITE32(open->op_share_access); + WRITE32(0); /* for us, share_deny== 0 always */ + WRITE64(NFS4_CLIENTID(cp->server)); + WRITE32(4); + WRITE32(id); + WRITE32(open->op_opentype); + ADJUST_ARGS(); + + if (open->op_opentype == NFS4_OPEN_CREATE) { + if (open->op_createmode == NFS4_CREATE_EXCLUSIVE) { + RESERVE_SPACE(12); + WRITE32(open->op_createmode); + WRITEMEM(open->op_verifier, sizeof(nfs4_verifier)); + ADJUST_ARGS(); + } + else if (open->op_attrs) { + RESERVE_SPACE(4); + WRITE32(open->op_createmode); + ADJUST_ARGS(); + if ((status = encode_attrs(cp, open->op_attrs))) + return status; + } + else { + RESERVE_SPACE(12); + WRITE32(open->op_createmode); + WRITE32(0); + WRITE32(0); + ADJUST_ARGS(); + } + } + + RESERVE_SPACE(8 + open->op_name->len); + WRITE32(NFS4_OPEN_CLAIM_NULL); + WRITE32(open->op_name->len); + WRITEMEM(open->op_name->name, open->op_name->len); + ADJUST_ARGS(); + + ENCODE_TAIL; +} + +static int +encode_open_confirm(struct nfs4_compound *cp, struct nfs4_open_confirm *open_confirm) +{ + ENCODE_HEAD; + + /* + * Note: In this "stateless" implementation, the OPEN_CONFIRM + * seqid is always equal to 1. + */ + RESERVE_SPACE(24); + WRITE32(OP_OPEN_CONFIRM); + WRITEMEM(open_confirm->oc_stateid, sizeof(nfs4_stateid)); + WRITE32(1); + ADJUST_ARGS(); + + ENCODE_TAIL; +} + +static int +encode_putfh(struct nfs4_compound *cp, struct nfs4_putfh *putfh) +{ + int len = putfh->pf_fhandle->size; + ENCODE_HEAD; + + RESERVE_SPACE(8 + len); + WRITE32(OP_PUTFH); + WRITE32(len); + WRITEMEM(putfh->pf_fhandle->data, len); + ADJUST_ARGS(); + + ENCODE_TAIL; +} + +static int +encode_putrootfh(struct nfs4_compound *cp) +{ + ENCODE_HEAD; + + RESERVE_SPACE(4); + WRITE32(OP_PUTROOTFH); + ADJUST_ARGS(); + + ENCODE_TAIL; +} + +static int +encode_read(struct nfs4_compound *cp, struct nfs4_read *read, struct rpc_rqst *req) +{ + struct rpc_auth *auth = req->rq_task->tk_auth; + int replen; + ENCODE_HEAD; + + RESERVE_SPACE(32); + WRITE32(OP_READ); + WRITE32(0); /* all-zero stateid! */ + WRITE32(0); + WRITE32(0); + WRITE32(0); + WRITE64(read->rd_offset); + WRITE32(read->rd_length); + ADJUST_ARGS(); + + /* set up reply iovec + * toplevel status + taglen + rescount + OP_PUTFH + status + * + OP_READ + status + eof + datalen = 9 + */ + replen = (RPC_REPHDRSIZE + auth->au_rslack + 9 + XDR_QUADLEN(cp->taglen)) << 2; + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + xdr_inline_pages(&req->rq_rcv_buf, replen, + read->rd_pages, read->rd_pgbase, read->rd_length); + + ENCODE_TAIL; +} + +static int +encode_readdir(struct nfs4_compound *cp, struct nfs4_readdir *readdir, struct rpc_rqst *req) +{ + struct rpc_auth *auth = req->rq_task->tk_auth; + int replen; + ENCODE_HEAD; + + RESERVE_SPACE(40); + WRITE32(OP_READDIR); + WRITE64(readdir->rd_cookie); + WRITEMEM(readdir->rd_req_verifier, sizeof(nfs4_verifier)); + WRITE32(readdir->rd_count >> 5); /* meaningless "dircount" field */ + WRITE32(readdir->rd_count); + WRITE32(2); + WRITE32(readdir->rd_bmval[0]); + WRITE32(readdir->rd_bmval[1]); + ADJUST_ARGS(); + + /* set up reply iovec + * toplevel_status + taglen + rescount + OP_PUTFH + status + * + OP_READDIR + status + verifer(2) = 9 + */ + replen = (RPC_REPHDRSIZE + auth->au_rslack + 9 + XDR_QUADLEN(cp->taglen)) << 2; + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + xdr_inline_pages(&req->rq_rcv_buf, replen, readdir->rd_pages, + readdir->rd_pgbase, readdir->rd_count); + + ENCODE_TAIL; +} + +static int +encode_readlink(struct nfs4_compound *cp, struct nfs4_readlink *readlink, struct rpc_rqst *req) +{ + struct rpc_auth *auth = req->rq_task->tk_auth; + int replen; + ENCODE_HEAD; + + RESERVE_SPACE(4); + WRITE32(OP_READLINK); + ADJUST_ARGS(); + + /* set up reply iovec + * toplevel_status + taglen + rescount + OP_PUTFH + status + * + OP_READLINK + status = 7 + */ + replen = (RPC_REPHDRSIZE + auth->au_rslack + 7 + XDR_QUADLEN(cp->taglen)) << 2; + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + xdr_inline_pages(&req->rq_rcv_buf, replen, readlink->rl_pages, 0, readlink->rl_count); + + ENCODE_TAIL; +} + +static int +encode_remove(struct nfs4_compound *cp, struct nfs4_remove *remove) +{ + ENCODE_HEAD; + + RESERVE_SPACE(8 + remove->rm_namelen); + WRITE32(OP_REMOVE); + WRITE32(remove->rm_namelen); + WRITEMEM(remove->rm_name, remove->rm_namelen); + ADJUST_ARGS(); + + ENCODE_TAIL; +} + +static int +encode_rename(struct nfs4_compound *cp, struct nfs4_rename *rename) +{ + ENCODE_HEAD; + + RESERVE_SPACE(8 + rename->rn_oldnamelen); + WRITE32(OP_RENAME); + WRITE32(rename->rn_oldnamelen); + WRITEMEM(rename->rn_oldname, rename->rn_oldnamelen); + ADJUST_ARGS(); + + RESERVE_SPACE(8 + rename->rn_newnamelen); + WRITE32(rename->rn_newnamelen); + WRITEMEM(rename->rn_newname, rename->rn_newnamelen); + ADJUST_ARGS(); + + ENCODE_TAIL; +} + +static int +encode_renew(struct nfs4_compound *cp) +{ + ENCODE_HEAD; + + RESERVE_SPACE(12); + WRITE32(OP_RENEW); + WRITE64(NFS4_CLIENTID(cp->server)); + ADJUST_ARGS(); + + ENCODE_TAIL; +} + +static int +encode_restorefh(struct nfs4_compound *cp) +{ + ENCODE_HEAD; + + RESERVE_SPACE(4); + WRITE32(OP_RESTOREFH); + ADJUST_ARGS(); + + ENCODE_TAIL; +} + +static int +encode_savefh(struct nfs4_compound *cp) +{ + ENCODE_HEAD; + + RESERVE_SPACE(4); + WRITE32(OP_SAVEFH); + ADJUST_ARGS(); + + ENCODE_TAIL; +} + +static int +encode_setattr(struct nfs4_compound *cp, struct nfs4_setattr *setattr) +{ + int status; + ENCODE_HEAD; + + RESERVE_SPACE(20); + WRITE32(OP_SETATTR); + WRITEMEM(setattr->st_stateid, sizeof(nfs4_stateid)); + ADJUST_ARGS(); + + if ((status = encode_attrs(cp, setattr->st_iap))) + return status; + + ENCODE_TAIL; +} + +static int +encode_setclientid(struct nfs4_compound *cp, struct nfs4_setclientid *setclientid) +{ + u32 total_len; + u32 len1, len2, len3; + ENCODE_HEAD; + + len1 = strlen(setclientid->sc_name); + len2 = strlen(setclientid->sc_netid); + len3 = strlen(setclientid->sc_uaddr); + total_len = XDR_QUADLEN(len1) + XDR_QUADLEN(len2) + XDR_QUADLEN(len3); + total_len = (total_len << 2) + 32; + + RESERVE_SPACE(total_len); + WRITE32(OP_SETCLIENTID); + WRITEMEM(setclientid->sc_verifier, sizeof(nfs4_verifier)); + WRITE32(len1); + WRITEMEM(setclientid->sc_name, len1); + WRITE32(setclientid->sc_prog); + WRITE32(len2); + WRITEMEM(setclientid->sc_netid, len2); + WRITE32(len3); + WRITEMEM(setclientid->sc_uaddr, len3); + WRITE32(setclientid->sc_cb_ident); + ADJUST_ARGS(); + + ENCODE_TAIL; +} + +static int +encode_setclientid_confirm(struct nfs4_compound *cp) +{ + ENCODE_HEAD; + + RESERVE_SPACE(12 + sizeof(nfs4_verifier)); + WRITE32(OP_SETCLIENTID_CONFIRM); + WRITE64(cp->server->nfs4_state->cl_clientid); + WRITEMEM(cp->server->nfs4_state->cl_confirm,sizeof(nfs4_verifier)); + ADJUST_ARGS(); + + ENCODE_TAIL; +} + +static int +encode_write(struct nfs4_compound *cp, struct nfs4_write *write, struct rpc_rqst *req) +{ + struct xdr_buf *sndbuf = &req->rq_snd_buf; + ENCODE_HEAD; + + RESERVE_SPACE(36); + WRITE32(OP_WRITE); + WRITE32(0xffffffff); /* magic stateid -1 */ + WRITE32(0xffffffff); + WRITE32(0xffffffff); + WRITE32(0xffffffff); + WRITE64(write->wr_offset); + WRITE32(write->wr_stable_how); + WRITE32(write->wr_len); + ADJUST_ARGS(); + + sndbuf->len = xdr_adjust_iovec(sndbuf->head, p); + xdr_encode_pages(sndbuf, write->wr_pages, write->wr_pgbase, write->wr_len); + + ENCODE_TAIL; +} + +static int +encode_compound(struct nfs4_compound *cp, struct rpc_rqst *req) +{ + int i, status = 0; + ENCODE_HEAD; + + dprintk("encode_compound: tag=%.*s\n", (int)cp->taglen, cp->tag); + + RESERVE_SPACE(12 + cp->taglen); + WRITE32(cp->taglen); + WRITEMEM(cp->tag, cp->taglen); + WRITE32(NFS4_MINOR_VERSION); + WRITE32(cp->req_nops); + ADJUST_ARGS(); + + for (i = 0; i < cp->req_nops; i++) { + switch (cp->ops[i].opnum) { + case OP_ACCESS: + status = encode_access(cp, &cp->ops[i].u.access); + break; + case OP_CLOSE: + status = encode_close(cp, &cp->ops[i].u.close); + break; + case OP_COMMIT: + status = encode_commit(cp, &cp->ops[i].u.commit); + break; + case OP_CREATE: + status = encode_create(cp, &cp->ops[i].u.create); + break; + case OP_GETATTR: + status = encode_getattr(cp, &cp->ops[i].u.getattr); + break; + case OP_GETFH: + status = encode_getfh(cp); + break; + case OP_LINK: + status = encode_link(cp, &cp->ops[i].u.link); + break; + case OP_LOOKUP: + status = encode_lookup(cp, &cp->ops[i].u.lookup); + break; + case OP_OPEN: + status = encode_open(cp, &cp->ops[i].u.open); + break; + case OP_OPEN_CONFIRM: + status = encode_open_confirm(cp, &cp->ops[i].u.open_confirm); + break; + case OP_PUTFH: + status = encode_putfh(cp, &cp->ops[i].u.putfh); + break; + case OP_PUTROOTFH: + status = encode_putrootfh(cp); + break; + case OP_READ: + status = encode_read(cp, &cp->ops[i].u.read, req); + break; + case OP_READDIR: + status = encode_readdir(cp, &cp->ops[i].u.readdir, req); + break; + case OP_READLINK: + status = encode_readlink(cp, &cp->ops[i].u.readlink, req); + break; + case OP_REMOVE: + status = encode_remove(cp, &cp->ops[i].u.remove); + break; + case OP_RENAME: + status = encode_rename(cp, &cp->ops[i].u.rename); + break; + case OP_RENEW: + status = encode_renew(cp); + break; + case OP_RESTOREFH: + status = encode_restorefh(cp); + break; + case OP_SAVEFH: + status = encode_savefh(cp); + break; + case OP_SETATTR: + status = encode_setattr(cp, &cp->ops[i].u.setattr); + break; + case OP_SETCLIENTID: + status = encode_setclientid(cp, &cp->ops[i].u.setclientid); + break; + case OP_SETCLIENTID_CONFIRM: + status = encode_setclientid_confirm(cp); + break; + case OP_WRITE: + status = encode_write(cp, &cp->ops[i].u.write, req); + break; + default: + BUG(); + } + if (status) + return status; + } + + ENCODE_TAIL; +} +/* + * END OF "GENERIC" ENCODE ROUTINES. + */ + + +/* + * Encode void argument + */ +static int +nfs4_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy) +{ + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + return 0; +} + +/* + * Encode COMPOUND argument + */ +static int +nfs4_xdr_enc_compound(struct rpc_rqst *req, u32 *p, struct nfs4_compound *cp) +{ + int status; + struct xdr_buf *sndbuf = &req->rq_snd_buf; + + cp->p = p; + cp->end = (u32 *) ((char *)req->rq_svec[0].iov_base + req->rq_svec[0].iov_len); + status = encode_compound(cp, req); + cp->timestamp = jiffies; + + if (!status && !sndbuf->page_len) + req->rq_slen = xdr_adjust_iovec(sndbuf->head, cp->p); + return status; +} + + +/* + * START OF "GENERIC" DECODE ROUTINES. + * These may look a little ugly since they are imported from a "generic" + * set of XDR encode/decode routines which are intended to be shared by + * all of our NFSv4 implementations (OpenBSD, MacOS X...). + * + * If the pain of reading these is too great, it should be a straightforward + * task to translate them into Linux-specific versions which are more + * consistent with the style used in NFSv2/v3... + */ +#define DECODE_HEAD \ + u32 *p; \ + int status +#define DECODE_TAIL \ + status = 0; \ +out: \ + return status; \ +xdr_error: \ + printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); \ + status = -EIO; \ + goto out + +#define READ32(x) (x) = ntohl(*p++) +#define READ64(x) do { \ + (x) = (u64)ntohl(*p++) << 32; \ + (x) |= ntohl(*p++); \ +} while (0) +#define READTIME(x) do { \ + p++; \ + (x) = (u64)ntohl(*p++) << 32; \ + (x) |= ntohl(*p++); \ +} while (0) +#define COPYMEM(x,nbytes) do { \ + memcpy((x), p, nbytes); \ + p += XDR_QUADLEN(nbytes); \ +} while (0) + +#define READ_BUF(nbytes) do { \ + if (nbytes > (u32)((char *)cp->end - (char *)cp->p)) \ + goto xdr_error; \ + p = cp->p; \ + cp->p += XDR_QUADLEN(nbytes); \ +} while (0) + +/* + * FIXME: The following dummy entry will be replaced once the userland + * upcall gets in... + */ +static int +decode_uid(char *p, u32 len, uid_t *uid) +{ + *uid = -2; + return 0; +} + +/* + * FIXME: The following dummy entry will be replaced once the userland + * upcall gets in... + */ +static int +decode_gid(char *p, u32 len, gid_t *gid) +{ + *gid = -2; + return 0; +} + +static int +decode_change_info(struct nfs4_compound *cp, struct nfs4_change_info *cinfo) +{ + DECODE_HEAD; + + READ_BUF(20); + READ32(cinfo->atomic); + READ64(cinfo->before); + READ64(cinfo->after); + + DECODE_TAIL; +} + +static int +decode_access(struct nfs4_compound *cp, int nfserr, struct nfs4_access *access) +{ + u32 supp, acc; + DECODE_HEAD; + + if (!nfserr) { + READ_BUF(8); + READ32(supp); + READ32(acc); + + status = -EIO; + if ((supp & ~access->ac_req_access) || (acc & ~supp)) { + printk(KERN_NOTICE "NFS: server returned bad bits in access call!\n"); + goto out; + } + *access->ac_resp_supported = supp; + *access->ac_resp_access = acc; + } + + DECODE_TAIL; +} + +static int +decode_close(struct nfs4_compound *cp, int nfserr, struct nfs4_close *close) +{ + DECODE_HEAD; + + if (!nfserr) { + READ_BUF(sizeof(nfs4_stateid)); + COPYMEM(close->cl_stateid, sizeof(nfs4_stateid)); + } + + DECODE_TAIL; +} + +static int +decode_commit(struct nfs4_compound *cp, int nfserr, struct nfs4_commit *commit) +{ + DECODE_HEAD; + + if (!nfserr) { + READ_BUF(8); + COPYMEM(commit->co_verifier->verifier, 8); + } + + DECODE_TAIL; +} + +static int +decode_create(struct nfs4_compound *cp, int nfserr, struct nfs4_create *create) +{ + u32 bmlen; + DECODE_HEAD; + + if (!nfserr) { + if ((status = decode_change_info(cp, create->cr_cinfo))) + goto out; + READ_BUF(4); + READ32(bmlen); + if (bmlen > 2) + goto xdr_error; + READ_BUF(bmlen << 2); + } + + DECODE_TAIL; +} + +extern u32 nfs4_fattr_bitmap[2]; +extern u32 nfs4_fsinfo_bitmap[2]; +extern u32 nfs4_fsstat_bitmap[2]; +extern u32 nfs4_pathconf_bitmap[2]; + +static int +decode_getattr(struct nfs4_compound *cp, int nfserr, struct nfs4_getattr *getattr) +{ + struct nfs_fattr *nfp = getattr->gt_attrs; + struct nfs_fsstat *fsstat = getattr->gt_fsstat; + struct nfs_fsinfo *fsinfo = getattr->gt_fsinfo; + struct nfs_pathconf *pathconf = getattr->gt_pathconf; + u32 bmlen; + u32 bmval0 = 0; + u32 bmval1 = 0; + u32 attrlen; + u32 dummy32; + u32 len = 0; + unsigned int type; + int fmode = 0; + DECODE_HEAD; + + if (nfserr) + goto success; + + READ_BUF(4); + READ32(bmlen); + if (bmlen > 2) + goto xdr_error; + + READ_BUF((bmlen << 2) + 4); + if (bmlen > 0) + READ32(bmval0); + if (bmlen > 1) + READ32(bmval1); + READ32(attrlen); + + if ((bmval0 & ~getattr->gt_bmval[0]) || + (bmval1 & ~getattr->gt_bmval[1])) { + dprintk("read_attrs: server returned bad attributes!\n"); + goto xdr_error; + } + getattr->gt_bmres[0] = bmval0; + getattr->gt_bmres[1] = bmval1; + + /* + * In case the server doesn't return some attributes, + * we initialize them here to some nominal values.. + */ + if (nfp) { + nfp->valid = NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3 | NFS_ATTR_FATTR_V4; + nfp->nlink = 1; + nfp->timestamp = jiffies; + } + if (fsinfo) { + fsinfo->rtmult = fsinfo->wtmult = 512; /* ??? */ + fsinfo->lease_time = 60; + } + + if (bmval0 & FATTR4_WORD0_TYPE) { + READ_BUF(4); + len += 4; + READ32(type); + if (type < NF4REG || type > NF4NAMEDATTR) { + dprintk("read_attrs: bad type %d\n", type); + goto xdr_error; + } + nfp->type = nfs_type2fmt[type].nfs2type; + fmode = nfs_type2fmt[type].mode; + dprintk("read_attrs: type=%d\n", (u32)nfp->type); + } + if (bmval0 & FATTR4_WORD0_CHANGE) { + READ_BUF(8); + len += 8; + READ64(nfp->change_attr); + dprintk("read_attrs: changeid=%Ld\n", (u64)nfp->change_attr); + } + if (bmval0 & FATTR4_WORD0_SIZE) { + READ_BUF(8); + len += 8; + READ64(nfp->size); + dprintk("read_attrs: size=%Ld\n", (u64)nfp->size); + } + if (bmval0 & FATTR4_WORD0_FSID) { + READ_BUF(16); + len += 16; + READ64(nfp->fsid_u.nfs4.major); + READ64(nfp->fsid_u.nfs4.minor); + dprintk("read_attrs: fsid=0x%Lx/0x%Lx\n", + nfp->fsid_u.nfs4.major, nfp->fsid_u.nfs4.minor); + } + if (bmval0 & FATTR4_WORD0_LEASE_TIME) { + READ_BUF(4); + len += 4; + READ32(fsinfo->lease_time); + dprintk("read_attrs: lease_time=%d\n", fsinfo->lease_time); + } + if (bmval0 & FATTR4_WORD0_FILEID) { + READ_BUF(8); + len += 8; + READ64(nfp->fileid); + dprintk("read_attrs: fileid=%Ld\n", nfp->fileid); + } + if (bmval0 & FATTR4_WORD0_FILES_AVAIL) { + READ_BUF(8); + len += 8; + READ64(fsstat->afiles); + dprintk("read_attrs: files_avail=0x%Lx\n", fsstat->afiles); + } + if (bmval0 & FATTR4_WORD0_FILES_FREE) { + READ_BUF(8); + len += 8; + READ64(fsstat->ffiles); + dprintk("read_attrs: files_free=0x%Lx\n", fsstat->ffiles); + } + if (bmval0 & FATTR4_WORD0_FILES_TOTAL) { + READ_BUF(8); + len += 8; + READ64(fsstat->tfiles); + dprintk("read_attrs: files_tot=0x%Lx\n", fsstat->tfiles); + } + if (bmval0 & FATTR4_WORD0_MAXFILESIZE) { + READ_BUF(8); + len += 8; + READ64(fsinfo->maxfilesize); + dprintk("read_attrs: maxfilesize=0x%Lx\n", fsinfo->maxfilesize); + } + if (bmval0 & FATTR4_WORD0_MAXLINK) { + READ_BUF(4); + len += 4; + READ32(pathconf->max_link); + dprintk("read_attrs: maxlink=%d\n", pathconf->max_link); + } + if (bmval0 & FATTR4_WORD0_MAXNAME) { + READ_BUF(4); + len += 4; + READ32(pathconf->max_namelen); + dprintk("read_attrs: maxname=%d\n", pathconf->max_namelen); + } + if (bmval0 & FATTR4_WORD0_MAXREAD) { + READ_BUF(8); + len += 8; + READ64(fsinfo->rtmax); + fsinfo->rtpref = fsinfo->dtpref = fsinfo->rtmax; + dprintk("read_attrs: maxread=%d\n", fsinfo->rtmax); + } + if (bmval0 & FATTR4_WORD0_MAXWRITE) { + READ_BUF(8); + len += 8; + READ64(fsinfo->wtmax); + fsinfo->wtpref = fsinfo->wtmax; + dprintk("read_attrs: maxwrite=%d\n", fsinfo->wtmax); + } + + if (bmval1 & FATTR4_WORD1_MODE) { + READ_BUF(4); + len += 4; + READ32(dummy32); + nfp->mode = (dummy32 & ~S_IFMT) | fmode; + dprintk("read_attrs: mode=0%o\n", nfp->mode); + } + if (bmval1 & FATTR4_WORD1_NUMLINKS) { + READ_BUF(4); + len += 4; + READ32(nfp->nlink); + dprintk("read_attrs: nlinks=0%o\n", nfp->nlink); + } + if (bmval1 & FATTR4_WORD1_OWNER) { + READ_BUF(4); + len += 4; + READ32(dummy32); /* name length */ + if (dummy32 > XDR_MAX_NETOBJ) { + dprintk("read_attrs: name too long!\n"); + goto xdr_error; + } + READ_BUF(dummy32); + len += (XDR_QUADLEN(dummy32) << 2); + if ((status = decode_uid((char *)p, dummy32, &nfp->uid))) { + dprintk("read_attrs: gss_get_num failed!\n"); + goto out; + } + dprintk("read_attrs: uid=%d\n", (int)nfp->uid); + } + if (bmval1 & FATTR4_WORD1_OWNER_GROUP) { + READ_BUF(4); + len += 4; + READ32(dummy32); + if (dummy32 > XDR_MAX_NETOBJ) { + dprintk("read_attrs: name too long!\n"); + goto xdr_error; + } + READ_BUF(dummy32); + len += (XDR_QUADLEN(dummy32) << 2); + if ((status = decode_gid((char *)p, dummy32, &nfp->gid))) { + dprintk("read_attrs: gss_get_num failed!\n"); + goto out; + } + dprintk("read_attrs: gid=%d\n", (int)nfp->gid); + } + if (bmval1 & FATTR4_WORD1_RAWDEV) { + READ_BUF(8); + len += 8; + READ32(dummy32); + nfp->rdev = (dummy32 << MINORBITS); + READ32(dummy32); + nfp->rdev |= (dummy32 & MINORMASK); + dprintk("read_attrs: rdev=%d\n", nfp->rdev); + } + if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) { + READ_BUF(8); + len += 8; + READ64(fsstat->abytes); + dprintk("read_attrs: savail=0x%Lx\n", fsstat->abytes); + } + if (bmval1 & FATTR4_WORD1_SPACE_FREE) { + READ_BUF(8); + len += 8; + READ64(fsstat->fbytes); + dprintk("read_attrs: sfree=0x%Lx\n", fsstat->fbytes); + } + if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) { + READ_BUF(8); + len += 8; + READ64(fsstat->tbytes); + dprintk("read_attrs: stotal=0x%Lx\n", fsstat->tbytes); + } + if (bmval1 & FATTR4_WORD1_SPACE_USED) { + READ_BUF(8); + len += 8; + READ64(nfp->du.nfs3.used); + dprintk("read_attrs: sused=0x%Lx\n", nfp->du.nfs3.used); + } + if (bmval1 & FATTR4_WORD1_TIME_ACCESS) { + READ_BUF(12); + len += 12; + READTIME(nfp->atime); + dprintk("read_attrs: atime=%d\n", (int)nfp->atime); + } + if (bmval1 & FATTR4_WORD1_TIME_METADATA) { + READ_BUF(12); + len += 12; + READTIME(nfp->ctime); + dprintk("read_attrs: ctime=%d\n", (int)nfp->ctime); + } + if (bmval1 & FATTR4_WORD1_TIME_MODIFY) { + READ_BUF(12); + len += 12; + READTIME(nfp->mtime); + dprintk("read_attrs: mtime=%d\n", (int)nfp->mtime); + } + if (len != attrlen) + goto xdr_error; + +success: + DECODE_TAIL; +} + +static int +decode_getfh(struct nfs4_compound *cp, int nfserr, struct nfs4_getfh *getfh) +{ + struct nfs_fh *fh = getfh->gf_fhandle; + int len; + DECODE_HEAD; + + /* Zero handle first to allow comparisons */ + memset(fh, 0, sizeof(*fh)); + + if (!nfserr) { + READ_BUF(4); + READ32(len); + if (len > NFS_MAXFHSIZE) + goto xdr_error; + fh->size = len; + READ_BUF(len); + COPYMEM(fh->data, len); + } + + DECODE_TAIL; +} + +static int +decode_link(struct nfs4_compound *cp, int nfserr, struct nfs4_link *link) +{ + int status = 0; + + if (!nfserr) + status = decode_change_info(cp, link->ln_cinfo); + return status; +} + +static int +decode_open(struct nfs4_compound *cp, int nfserr, struct nfs4_open *open) +{ + u32 bmlen, delegation_type; + DECODE_HEAD; + + if (!nfserr) { + READ_BUF(sizeof(nfs4_stateid)); + COPYMEM(open->op_stateid, sizeof(nfs4_stateid)); + + decode_change_info(cp, open->op_cinfo); + + READ_BUF(8); + READ32(*open->op_rflags); + READ32(bmlen); + if (bmlen > 10) + goto xdr_error; + + READ_BUF((bmlen << 2) + 4); + p += bmlen; + READ32(delegation_type); + if (delegation_type != NFS4_OPEN_DELEGATE_NONE) + goto xdr_error; + } + + DECODE_TAIL; +} + +static int +decode_open_confirm(struct nfs4_compound *cp, int nfserr, struct nfs4_open_confirm *open_confirm) +{ + DECODE_HEAD; + + if (!nfserr) { + READ_BUF(sizeof(nfs4_stateid)); + COPYMEM(open_confirm->oc_stateid, sizeof(nfs4_stateid)); + } + + DECODE_TAIL; +} + +static int +decode_read(struct nfs4_compound *cp, int nfserr, struct nfs4_read *read) +{ + u32 throwaway; + DECODE_HEAD; + + if (!nfserr) { + READ_BUF(8); + if (read->rd_eof) + READ32(*read->rd_eof); + else + READ32(throwaway); + READ32(*read->rd_bytes_read); + if (*read->rd_bytes_read > read->rd_length) + goto xdr_error; + } + + DECODE_TAIL; +} + +static int +decode_readdir(struct nfs4_compound *cp, int nfserr, struct rpc_rqst *req, struct nfs4_readdir *readdir) +{ + struct xdr_buf *rcvbuf = &req->rq_rcv_buf; + struct page *page = *rcvbuf->pages; + unsigned int pglen = rcvbuf->page_len; + u32 *end, *entry; + u32 len, attrlen, word; + int i; + DECODE_HEAD; + + if (!nfserr) { + READ_BUF(8); + COPYMEM(readdir->rd_resp_verifier, 8); + + BUG_ON(pglen > PAGE_CACHE_SIZE); + p = (u32 *) kmap(page); + end = (u32 *) ((char *)p + pglen + readdir->rd_pgbase); + + while (*p++) { + entry = p - 1; + if (p + 3 > end) + goto short_pkt; + p += 2; /* cookie */ + len = ntohl(*p++); /* filename length */ + if (len > NFS4_MAXNAMLEN) { + printk(KERN_WARNING "NFS: giant filename in readdir (len 0x%x)\n", len); + goto err_unmap; + } + + p += XDR_QUADLEN(len); + if (p + 1 > end) + goto short_pkt; + len = ntohl(*p++); /* bitmap length */ + if (len > 10) { + printk(KERN_WARNING "NFS: giant bitmap in readdir (len 0x%x)\n", len); + goto err_unmap; + } + if (p + len + 1 > end) + goto short_pkt; + attrlen = 0; + for (i = 0; i < len; i++) { + word = ntohl(*p++); + if (!word) + continue; + else if (i == 0 && word == FATTR4_WORD0_FILEID) { + attrlen = 8; + continue; + } + printk(KERN_WARNING "NFS: unexpected bitmap word in readdir (0x%x)\n", word); + goto err_unmap; + } + if (ntohl(*p++) != attrlen) { + printk(KERN_WARNING "NFS: unexpected attrlen in readdir\n"); + goto err_unmap; + } + p += XDR_QUADLEN(attrlen); + if (p + 1 > end) + goto short_pkt; + } + kunmap(page); + } + + DECODE_TAIL; +short_pkt: + printk(KERN_NOTICE "NFS: short packet in readdir reply!\n"); + /* truncate listing */ + kunmap(page); + entry[0] = entry[1] = 0; + return 0; +err_unmap: + kunmap(page); + return -errno_NFSERR_IO; +} + +static int +decode_readlink(struct nfs4_compound *cp, int nfserr, struct rpc_rqst *req, struct nfs4_readlink *readlink) +{ + struct xdr_buf *rcvbuf = &req->rq_rcv_buf; + u32 *strlen; + u32 len; + char *string; + + if (!nfserr) { + /* + * The XDR encode routine has set things up so that + * the link text will be copied directly into the + * buffer. We just have to do overflow-checking, + * and and null-terminate the text (the VFS expects + * null-termination). + */ + strlen = (u32 *) kmap(rcvbuf->pages[0]); + len = ntohl(*strlen); + if (len > PAGE_CACHE_SIZE - 5) { + printk(KERN_WARNING "nfs: server returned giant symlink!\n"); + kunmap(rcvbuf->pages[0]); + return -EIO; + } + *strlen = len; + + string = (char *)(strlen + 1); + string[len] = '\0'; + kunmap(rcvbuf->pages[0]); + } + return 0; +} + +static int +decode_remove(struct nfs4_compound *cp, int nfserr, struct nfs4_remove *remove) +{ + int status; + + status = 0; + if (!nfserr) + status = decode_change_info(cp, remove->rm_cinfo); + return status; +} + +static int +decode_rename(struct nfs4_compound *cp, int nfserr, struct nfs4_rename *rename) +{ + int status = 0; + + if (!nfserr) { + if ((status = decode_change_info(cp, rename->rn_src_cinfo))) + goto out; + if ((status = decode_change_info(cp, rename->rn_dst_cinfo))) + goto out; + } +out: + return status; +} + +static int +decode_setattr(struct nfs4_compound *cp) +{ + u32 bmlen; + DECODE_HEAD; + + READ_BUF(4); + READ32(bmlen); + if (bmlen > 10) + goto xdr_error; + READ_BUF(bmlen << 2); + + DECODE_TAIL; +} + +static int +decode_setclientid(struct nfs4_compound *cp, int nfserr) +{ + DECODE_HEAD; + + if (!nfserr) { + READ_BUF(8 + sizeof(nfs4_verifier)); + READ64(cp->server->nfs4_state->cl_clientid); + COPYMEM(cp->server->nfs4_state->cl_confirm, sizeof(nfs4_verifier)); + } + else if (nfserr == NFSERR_CLID_INUSE) { + u32 len; + + /* skip netid string */ + READ_BUF(4); + READ32(len); + READ_BUF(len); + + /* skip uaddr string */ + READ_BUF(4); + READ32(len); + READ_BUF(len); + } + + DECODE_TAIL; +} + +static int +decode_write(struct nfs4_compound *cp, int nfserr, struct nfs4_write *write) +{ + DECODE_HEAD; + + if (!nfserr) { + READ_BUF(16); + READ32(*write->wr_bytes_written); + if (*write->wr_bytes_written > write->wr_len) + goto xdr_error; + READ32(write->wr_verf->committed); + COPYMEM(write->wr_verf->verifier, 8); + } + + DECODE_TAIL; +} + +static int +decode_compound(struct nfs4_compound *cp, struct rpc_rqst *req) +{ + u32 taglen; + u32 opnum, nfserr; + DECODE_HEAD; + + READ_BUF(8); + READ32(cp->toplevel_status); + READ32(taglen); + + /* + * We need this if our zero-copy I/O is going to work. Rumor has + * it that the spec will soon mandate it... + */ + if (taglen != cp->taglen) + dprintk("nfs4: non-conforming server returns tag length mismatch!\n"); + + READ_BUF(taglen + 4); + p += XDR_QUADLEN(taglen); + READ32(cp->resp_nops); + if (cp->resp_nops > cp->req_nops) { + dprintk("nfs4: resp_nops > req_nops!\n"); + goto xdr_error; + } + + for (cp->nops = 0; cp->nops < cp->resp_nops; cp->nops++) { + READ_BUF(8); + READ32(opnum); + if (opnum != cp->ops[cp->nops].opnum) { + dprintk("nfs4: operation mismatch!\n"); + goto xdr_error; + } + READ32(nfserr); + if (cp->nops == cp->resp_nops - 1) { + if (nfserr != cp->toplevel_status) { + dprintk("nfs4: status mismatch!\n"); + goto xdr_error; + } + } + else if (nfserr) { + dprintk("nfs4: intermediate status nonzero!\n"); + goto xdr_error; + } + cp->ops[cp->nops].nfserr = nfserr; + + switch (opnum) { + case OP_ACCESS: + status = decode_access(cp, nfserr, &cp->ops[cp->nops].u.access); + break; + case OP_CLOSE: + status = decode_close(cp, nfserr, &cp->ops[cp->nops].u.close); + break; + case OP_COMMIT: + status = decode_commit(cp, nfserr, &cp->ops[cp->nops].u.commit); + break; + case OP_CREATE: + status = decode_create(cp, nfserr, &cp->ops[cp->nops].u.create); + break; + case OP_GETATTR: + status = decode_getattr(cp, nfserr, &cp->ops[cp->nops].u.getattr); + break; + case OP_GETFH: + status = decode_getfh(cp, nfserr, &cp->ops[cp->nops].u.getfh); + break; + case OP_LINK: + status = decode_link(cp, nfserr, &cp->ops[cp->nops].u.link); + break; + case OP_LOOKUP: + status = 0; + break; + case OP_OPEN: + status = decode_open(cp, nfserr, &cp->ops[cp->nops].u.open); + break; + case OP_OPEN_CONFIRM: + status = decode_open_confirm(cp, nfserr, &cp->ops[cp->nops].u.open_confirm); + break; + case OP_PUTFH: + status = 0; + break; + case OP_PUTROOTFH: + status = 0; + break; + case OP_READ: + status = decode_read(cp, nfserr, &cp->ops[cp->nops].u.read); + break; + case OP_READDIR: + status = decode_readdir(cp, nfserr, req, &cp->ops[cp->nops].u.readdir); + break; + case OP_READLINK: + status = decode_readlink(cp, nfserr, req, &cp->ops[cp->nops].u.readlink); + break; + case OP_RESTOREFH: + status = 0; + break; + case OP_REMOVE: + status = decode_remove(cp, nfserr, &cp->ops[cp->nops].u.remove); + break; + case OP_RENAME: + status = decode_rename(cp, nfserr, &cp->ops[cp->nops].u.rename); + break; + case OP_RENEW: + status = 0; + break; + case OP_SAVEFH: + status = 0; + break; + case OP_SETATTR: + status = decode_setattr(cp); + break; + case OP_SETCLIENTID: + status = decode_setclientid(cp, nfserr); + break; + case OP_SETCLIENTID_CONFIRM: + status = 0; + break; + case OP_WRITE: + status = decode_write(cp, nfserr, &cp->ops[cp->nops].u.write); + break; + default: + BUG(); + return -EIO; + } + if (status) + goto xdr_error; + } + + DECODE_TAIL; +} +/* + * END OF "GENERIC" DECODE ROUTINES. + */ + +/* + * Decode void reply + */ +static int +nfs4_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy) +{ + return 0; +} + +/* + * Decode COMPOUND response + */ +static int +nfs4_xdr_dec_compound(struct rpc_rqst *rqstp, u32 *p, struct nfs4_compound *cp) +{ + int status; + + cp->p = p; + cp->end = (u32 *) ((u8 *) rqstp->rq_rvec->iov_base + rqstp->rq_rvec->iov_len); + + if ((status = decode_compound(cp, rqstp))) + goto out; + + status = 0; + if (cp->toplevel_status) + status = -nfs_stat_to_errno(cp->toplevel_status); + +out: + return status; +} + +u32 * +nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus) +{ + u32 len; + + if (!*p++) { + if (!*p) + return ERR_PTR(-EAGAIN); + entry->eof = 1; + return ERR_PTR(-EBADCOOKIE); + } + + entry->prev_cookie = entry->cookie; + p = xdr_decode_hyper(p, &entry->cookie); + entry->len = ntohl(*p++); + entry->name = (const char *) p; + p += XDR_QUADLEN(entry->len); + + if (entry->cookie > COOKIE_MAX) + entry->cookie = COOKIE_MAX; + + /* + * In case the server doesn't return an inode number, + * we fake one here. (We don't use inode number 0, + * since glibc seems to choke on it...) + */ + entry->ino = 1; + + len = ntohl(*p++); /* bitmap length */ + p += len; + len = ntohl(*p++); /* attribute buffer length */ + if (len) + p = xdr_decode_hyper(p, &entry->ino); + + entry->eof = !p[0] && p[1]; + return p; +} + +#ifndef MAX +# define MAX(a, b) (((a) > (b))? (a) : (b)) +#endif + +#define PROC(proc, argtype, restype) \ + { "nfs4_" #proc, \ + (kxdrproc_t) nfs4_xdr_##argtype, \ + (kxdrproc_t) nfs4_xdr_##restype, \ + MAX(NFS4_##argtype##_sz,NFS4_##restype##_sz) << 2, \ + 0 \ + } + +static struct rpc_procinfo nfs4_procedures[] = { + PROC(null, enc_void, dec_void), + PROC(compound, enc_compound, dec_compound) +}; + +struct rpc_version nfs_version4 = { + .number = 4, + .nrprocs = sizeof(nfs4_procedures)/sizeof(nfs4_procedures[0]), + .procs = nfs4_procedures +}; + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 76ab4ecc3ea8..3a23ac81e80f 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -472,6 +472,25 @@ extern void * nfs_root_data(void); #define NFS_JUKEBOX_RETRY_TIME (5 * HZ) +#ifdef CONFIG_NFS_V4 +struct nfs4_client { + atomic_t cl_count; /* refcount */ + u64 cl_clientid; /* constant */ + nfs4_verifier cl_confirm; + + /* + * Starts a list of lockowners, linked through lo_list. + */ + struct list_head cl_lockowners; /* protected by state_spinlock */ +}; + +/* nfs4proc.c */ +extern int nfs4_proc_renew(struct nfs_server *server); + +/* nfs4renewd.c */ +extern int nfs4_init_renewd(struct nfs_server *server); +#endif /* CONFIG_NFS_V4 */ + #ifdef CONFIG_NFS_V4 extern struct nfs4_client *nfs4_get_client(void); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index e542fe6982c5..4bb5125056e7 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -325,6 +325,219 @@ struct nfs3_readdirres { int plus; }; +#ifdef CONFIG_NFS_V4 + +typedef u64 clientid4; + +struct nfs4_change_info { + u32 atomic; + u64 before; + u64 after; +}; + +struct nfs4_access { + u32 ac_req_access; /* request */ + u32 * ac_resp_supported; /* response */ + u32 * ac_resp_access; /* response */ +}; + +struct nfs4_close { + char * cl_stateid; /* request */ + u32 cl_seqid; /* request */ +}; + +struct nfs4_commit { + u64 co_start; /* request */ + u32 co_len; /* request */ + struct nfs_writeverf * co_verifier; /* response */ +}; + +struct nfs4_create { + u32 cr_ftype; /* request */ + union { /* request */ + struct { + u32 textlen; + const char * text; + } symlink; /* NF4LNK */ + struct { + u32 specdata1; + u32 specdata2; + } device; /* NF4BLK, NF4CHR */ + } u; + u32 cr_namelen; /* request */ + const char * cr_name; /* request */ + struct iattr * cr_attrs; /* request */ + struct nfs4_change_info * cr_cinfo; /* response */ +}; +#define cr_textlen u.symlink.textlen +#define cr_text u.symlink.text +#define cr_specdata1 u.device.specdata1 +#define cr_specdata2 u.device.specdata2 + +struct nfs4_getattr { + u32 * gt_bmval; /* request */ + struct nfs_fattr * gt_attrs; /* response */ + struct nfs_fsstat * gt_fsstat; /* response */ + struct nfs_fsinfo * gt_fsinfo; /* response */ + struct nfs_pathconf * gt_pathconf; /* response */ + u32 * gt_bmres; /* response */ +}; + +struct nfs4_getfh { + struct nfs_fh * gf_fhandle; /* response */ +}; + +struct nfs4_link { + u32 ln_namelen; /* request */ + const char * ln_name; /* request */ + struct nfs4_change_info * ln_cinfo; /* response */ +}; + +struct nfs4_lookup { + struct qstr * lo_name; /* request */ +}; + +struct nfs4_open { + u32 op_share_access; /* request */ + u32 op_opentype; /* request */ + u32 op_createmode; /* request */ + union { /* request */ + struct iattr * attrs; /* UNCHECKED, GUARDED */ + nfs4_verifier verifier; /* EXCLUSIVE */ + } u; + struct qstr * op_name; /* request */ + char * op_stateid; /* response */ + struct nfs4_change_info * op_cinfo; /* response */ + u32 * op_rflags; /* response */ +}; +#define op_attrs u.attrs +#define op_verifier u.verifier + +struct nfs4_open_confirm { + char * oc_stateid; /* request */ +}; + +struct nfs4_putfh { + struct nfs_fh * pf_fhandle; /* request */ +}; + +struct nfs4_read { + u64 rd_offset; /* request */ + u32 rd_length; /* request */ + u32 *rd_eof; /* response */ + u32 *rd_bytes_read; /* response */ + struct page ** rd_pages; /* zero-copy data */ + unsigned int rd_pgbase; /* zero-copy data */ +}; + +struct nfs4_readdir { + u64 rd_cookie; /* request */ + nfs4_verifier rd_req_verifier; /* request */ + u32 rd_count; /* request */ + u32 rd_bmval[2]; /* request */ + nfs4_verifier rd_resp_verifier; /* response */ + struct page ** rd_pages; /* zero-copy data */ + unsigned int rd_pgbase; /* zero-copy data */ +}; + +struct nfs4_readlink { + u32 rl_count; /* zero-copy data */ + struct page ** rl_pages; /* zero-copy data */ +}; + +struct nfs4_remove { + u32 rm_namelen; /* request */ + const char * rm_name; /* request */ + struct nfs4_change_info * rm_cinfo; /* response */ +}; + +struct nfs4_rename { + u32 rn_oldnamelen; /* request */ + const char * rn_oldname; /* request */ + u32 rn_newnamelen; /* request */ + const char * rn_newname; /* request */ + struct nfs4_change_info * rn_src_cinfo; /* response */ + struct nfs4_change_info * rn_dst_cinfo; /* response */ +}; + +struct nfs4_setattr { + char * st_stateid; /* request */ + struct iattr * st_iap; /* request */ +}; + +struct nfs4_setclientid { + nfs4_verifier sc_verifier; /* request */ + char * sc_name; /* request */ + u32 sc_prog; /* request */ + char sc_netid[4]; /* request */ + char sc_uaddr[24]; /* request */ + u32 sc_cb_ident; /* request */ +}; + +struct nfs4_write { + u64 wr_offset; /* request */ + u32 wr_stable_how; /* request */ + u32 wr_len; /* request */ + u32 * wr_bytes_written; /* response */ + struct nfs_writeverf * wr_verf; /* response */ + struct page ** wr_pages; /* zero-copy data */ + unsigned int wr_pgbase; /* zero-copy data */ +}; + +struct nfs4_op { + u32 opnum; + u32 nfserr; + union { + struct nfs4_access access; + struct nfs4_close close; + struct nfs4_commit commit; + struct nfs4_create create; + struct nfs4_getattr getattr; + struct nfs4_getfh getfh; + struct nfs4_link link; + struct nfs4_lookup lookup; + struct nfs4_open open; + struct nfs4_open_confirm open_confirm; + struct nfs4_putfh putfh; + struct nfs4_read read; + struct nfs4_readdir readdir; + struct nfs4_readlink readlink; + struct nfs4_remove remove; + struct nfs4_rename rename; + struct nfs4_setattr setattr; + struct nfs4_setclientid setclientid; + struct nfs4_write write; + } u; +}; + +struct nfs4_compound { + unsigned int flags; /* defined below */ + struct nfs_server * server; + + /* RENEW information */ + int renew_index; + unsigned long timestamp; + + /* scratch variables for XDR encode/decode */ + int nops; + u32 * p; + u32 * end; + + /* the individual COMPOUND operations */ + struct nfs4_op *ops; + + /* request */ + int req_nops; + u32 taglen; + char * tag; + + /* response */ + int resp_nops; + int toplevel_status; +}; + +#endif /* CONFIG_NFS_V4 */ + struct nfs_read_data { struct rpc_task task; struct inode *inode; @@ -338,7 +551,12 @@ struct nfs_read_data { struct nfs_readres res; } v3; /* also v2 */ #ifdef CONFIG_NFS_V4 - /* NFSv4 data will come here... */ + struct { + struct nfs4_compound compound; + struct nfs4_op ops[3]; + u32 res_count; + u32 res_eof; + } v4; #endif } u; }; @@ -353,11 +571,17 @@ struct nfs_write_data { struct page *pagevec[NFS_WRITE_MAXIOV]; union { struct { - struct nfs_writeargs args; - struct nfs_writeres res; + struct nfs_writeargs args; /* argument struct */ + struct nfs_writeres res; /* result struct */ } v3; #ifdef CONFIG_NFS_V4 - /* NFSv4 data to come here... */ + struct { + struct nfs4_compound compound; + struct nfs4_op ops[3]; + u32 arg_count; + u32 arg_stable; + u32 res_count; + } v4; #endif } u; }; @@ -430,8 +654,10 @@ struct nfs_rpc_ops { */ extern struct nfs_rpc_ops nfs_v2_clientops; extern struct nfs_rpc_ops nfs_v3_clientops; +extern struct nfs_rpc_ops nfs_v4_clientops; extern struct rpc_version nfs_version2; extern struct rpc_version nfs_version3; +extern struct rpc_version nfs_version4; extern struct rpc_program nfs_program; extern struct rpc_stat nfs_rpcstat; -- cgit v1.2.3 From 603a61b56832c3c70595eed5750ca75fcbf15a7a Mon Sep 17 00:00:00 2001 From: "Martin J. Bligh" Date: Tue, 15 Oct 2002 05:37:52 -0700 Subject: [PATCH] Summit: infrastructure This puts the DFR (desination format register) value into a #define, and calculates the LDR (logical desitination register) correctly dependant on platform. Similarly for TARGET_CPUS. --- arch/i386/kernel/apic.c | 6 ++---- arch/i386/mach-generic/mach_apic.h | 16 ++++++++++++++++ arch/i386/mach-summit/mach_apic.h | 22 ++++++++++++++++++++++ include/asm-i386/apicdef.h | 2 ++ include/asm-i386/smp.h | 13 +++---------- 5 files changed, 45 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c index 24e7a1e74f4a..d7998626bcc0 100644 --- a/arch/i386/kernel/apic.c +++ b/arch/i386/kernel/apic.c @@ -329,15 +329,13 @@ void __init setup_local_APIC (void) * Put the APIC into flat delivery mode. * Must be "all ones" explicitly for 82489DX. */ - apic_write_around(APIC_DFR, 0xffffffff); + apic_write_around(APIC_DFR, APIC_DFR_VALUE); /* * Set up the logical destination ID. */ value = apic_read(APIC_LDR); - value &= ~APIC_LDR_MASK; - value |= (1<<(smp_processor_id()+24)); - apic_write_around(APIC_LDR, value); + apic_write_around(APIC_LDR, calculate_ldr(value)); } /* diff --git a/arch/i386/mach-generic/mach_apic.h b/arch/i386/mach-generic/mach_apic.h index a5480d89b58c..5d6e15a51d79 100644 --- a/arch/i386/mach-generic/mach_apic.h +++ b/arch/i386/mach-generic/mach_apic.h @@ -1,4 +1,20 @@ #ifndef __ASM_MACH_APIC_H #define __ASM_MACH_APIC_H +static inline unsigned long calculate_ldr(unsigned long old) +{ + unsigned long id; + + id = 1UL << smp_processor_id(); + return ((old & ~APIC_LDR_MASK) | SET_APIC_LOGICAL_ID(id)); +} + +#define APIC_DFR_VALUE (APIC_DFR_FLAT) + +#ifdef CONFIG_SMP + #define TARGET_CPUS (clustered_apic_mode ? 0xf : cpu_online_map) +#else + #define TARGET_CPUS 0x01 +#endif + #endif /* __ASM_MACH_APIC_H */ diff --git a/arch/i386/mach-summit/mach_apic.h b/arch/i386/mach-summit/mach_apic.h index a5480d89b58c..f672f5bba209 100644 --- a/arch/i386/mach-summit/mach_apic.h +++ b/arch/i386/mach-summit/mach_apic.h @@ -1,4 +1,26 @@ #ifndef __ASM_MACH_APIC_H #define __ASM_MACH_APIC_H +extern int x86_summit; + +#define XAPIC_DEST_CPUS_MASK 0x0Fu +#define XAPIC_DEST_CLUSTER_MASK 0xF0u + +#define xapic_phys_to_log_apicid(phys_apic) ( (1ul << ((phys_apic) & 0x3)) |\ + ((phys_apic) & XAPIC_DEST_CLUSTER_MASK) ) + +static inline unsigned long calculate_ldr(unsigned long old) +{ + unsigned long id; + + if (x86_summit) + id = xapic_phys_to_log_apicid(hard_smp_processor_id()); + else + id = 1UL << smp_processor_id(); + return ((old & ~APIC_LDR_MASK) | SET_APIC_LOGICAL_ID(id)); +} + +#define APIC_DFR_VALUE (x86_summit ? APIC_DFR_CLUSTER : APIC_DFR_FLAT) +#define TARGET_CPUS (x86_summit ? XAPIC_DEST_CPUS_MASK : cpu_online_map) + #endif /* __ASM_MACH_APIC_H */ diff --git a/include/asm-i386/apicdef.h b/include/asm-i386/apicdef.h index a91e6ede6b0a..be81838ef1e9 100644 --- a/include/asm-i386/apicdef.h +++ b/include/asm-i386/apicdef.h @@ -32,6 +32,8 @@ #define SET_APIC_LOGICAL_ID(x) (((x)<<24)) #define APIC_ALL_CPUS 0xFF #define APIC_DFR 0xE0 +#define APIC_DFR_CLUSTER 0x0FFFFFFFul +#define APIC_DFR_FLAT 0xFFFFFFFFul #define APIC_SPIV 0xF0 #define APIC_SPIV_FOCUS_DISABLED (1<<9) #define APIC_SPIV_APIC_ENABLED (1<<8) diff --git a/include/asm-i386/smp.h b/include/asm-i386/smp.h index 71a71d011842..a0d552e5d843 100644 --- a/include/asm-i386/smp.h +++ b/include/asm-i386/smp.h @@ -21,17 +21,10 @@ #endif #endif -#ifdef CONFIG_SMP -# ifdef CONFIG_CLUSTERED_APIC -# define TARGET_CPUS 0xf /* all CPUs in *THIS* quad */ -# define INT_DELIVERY_MODE 0 /* physical delivery on LOCAL quad */ -# else -# define TARGET_CPUS cpu_online_map -# define INT_DELIVERY_MODE 1 /* logical delivery broadcast to all procs */ -# endif +#ifdef CONFIG_CLUSTERED_APIC + #define INT_DELIVERY_MODE 0 /* physical delivery on LOCAL quad */ #else -# define INT_DELIVERY_MODE 1 /* logical delivery */ -# define TARGET_CPUS 0x01 + #define INT_DELIVERY_MODE 1 /* logical delivery broadcast to all procs */ #endif #ifndef clustered_apic_mode -- cgit v1.2.3 From f0175f7fa5d8bad9567f4bbbac37aa34a0bbe408 Mon Sep 17 00:00:00 2001 From: "Martin J. Bligh" Date: Tue, 15 Oct 2002 05:37:57 -0700 Subject: [PATCH] Summit: APIC limits This one sets up the apic broadcast id (the maximum allowable apic address) properly for whichever platform. It also abstracts out check_apicid_used, because that check doesn't work on Summit. Oh, and I bumped up MAX_IO_APICS, but only for NUMA x86 platforms. --- arch/i386/kernel/io_apic.c | 5 +++-- arch/i386/mach-generic/mach_apic.h | 3 +++ arch/i386/mach-summit/mach_apic.h | 3 +++ include/asm-i386/apicdef.h | 6 +++++- 4 files changed, 14 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index b8fc20a24225..5bc54a30c2f8 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -1152,7 +1152,7 @@ static void __init setup_ioapic_ids_from_mpc (void) old_id = mp_ioapics[apic].mpc_apicid; - if (mp_ioapics[apic].mpc_apicid >= 0xf) { + if (mp_ioapics[apic].mpc_apicid >= APIC_BROADCAST_ID) { printk(KERN_ERR "BIOS bug, IO-APIC#%d ID is %d in the MPC table!...\n", apic, mp_ioapics[apic].mpc_apicid); printk(KERN_ERR "... fixing up to %d. (tell your hw vendor)\n", @@ -1165,7 +1165,8 @@ static void __init setup_ioapic_ids_from_mpc (void) * system must have a unique ID or we get lots of nice * 'stuck on smp_invalidate_needed IPI wait' messages. */ - if (phys_id_present_map & (1 << mp_ioapics[apic].mpc_apicid)) { + if (check_apicid_used(phys_id_present_map, + mp_ioapics[apic].mpc_apicid)) { printk(KERN_ERR "BIOS bug, IO-APIC#%d ID %d is already used!...\n", apic, mp_ioapics[apic].mpc_apicid); for (i = 0; i < 0xf; i++) diff --git a/arch/i386/mach-generic/mach_apic.h b/arch/i386/mach-generic/mach_apic.h index 5d6e15a51d79..ea38d35c8ded 100644 --- a/arch/i386/mach-generic/mach_apic.h +++ b/arch/i386/mach-generic/mach_apic.h @@ -17,4 +17,7 @@ static inline unsigned long calculate_ldr(unsigned long old) #define TARGET_CPUS 0x01 #endif +#define APIC_BROADCAST_ID 0x0F +#define check_apicid_used(bitmap, apicid) (bitmap & (1 << apicid)) + #endif /* __ASM_MACH_APIC_H */ diff --git a/arch/i386/mach-summit/mach_apic.h b/arch/i386/mach-summit/mach_apic.h index f672f5bba209..5b2a6999c968 100644 --- a/arch/i386/mach-summit/mach_apic.h +++ b/arch/i386/mach-summit/mach_apic.h @@ -23,4 +23,7 @@ static inline unsigned long calculate_ldr(unsigned long old) #define APIC_DFR_VALUE (x86_summit ? APIC_DFR_CLUSTER : APIC_DFR_FLAT) #define TARGET_CPUS (x86_summit ? XAPIC_DEST_CPUS_MASK : cpu_online_map) +#define APIC_BROADCAST_ID (x86_summit ? 0xFF : 0x0F) +#define check_apicid_used(bitmap, apicid) (0) + #endif /* __ASM_MACH_APIC_H */ diff --git a/include/asm-i386/apicdef.h b/include/asm-i386/apicdef.h index be81838ef1e9..4aac5f72e9ed 100644 --- a/include/asm-i386/apicdef.h +++ b/include/asm-i386/apicdef.h @@ -110,7 +110,11 @@ #define APIC_BASE (fix_to_virt(FIX_APIC_BASE)) -#define MAX_IO_APICS 8 +#ifdef CONFIG_X86_NUMA + #define MAX_IO_APICS 32 +#else + #define MAX_IO_APICS 8 +#endif /* * the local APIC register structure, memory mapped. Not terribly well -- cgit v1.2.3 From 246f38c2f955fee0a292c2673ae84dd96dc09913 Mon Sep 17 00:00:00 2001 From: "Martin J. Bligh" Date: Tue, 15 Oct 2002 05:38:07 -0700 Subject: [PATCH] Summit: APIC ID mapping Adds a raw_phys_apicid array that maps from the mps cpu number to the apicid - this is needed because the apicids for Summit can be larger than 32, and thus won't fit into the bitmap. Also adds little wrappers to map neatly between the two. Bumps up MAX_APICS for Summit. --- arch/i386/kernel/mpparse.c | 4 +++- arch/i386/kernel/smpboot.c | 1 + arch/i386/mach-generic/mach_apic.h | 13 +++++++++++++ arch/i386/mach-summit/mach_apic.h | 16 ++++++++++++++++ include/asm-i386/mpspec.h | 6 +++--- include/asm-i386/smp.h | 1 + include/asm-i386/smpboot.h | 9 --------- 7 files changed, 37 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/arch/i386/kernel/mpparse.c b/arch/i386/kernel/mpparse.c index 0768f1f09941..c9bdad8cb3f1 100644 --- a/arch/i386/kernel/mpparse.c +++ b/arch/i386/kernel/mpparse.c @@ -71,6 +71,7 @@ static unsigned int __initdata num_processors; unsigned long phys_cpu_present_map; int summit_x86 = 0; +u8 raw_phys_apicid[NR_CPUS] = { [0 ... NR_CPUS-1] = BAD_APICID }; /* * Intel MP BIOS table parsing routines: @@ -192,7 +193,7 @@ void __init MP_processor_info (struct mpc_config_processor *m) if (clustered_apic_mode) { phys_cpu_present_map |= (logical_apicid&0xf) << (4*quad); } else { - phys_cpu_present_map |= 1 << m->mpc_apicid; + phys_cpu_present_map |= apicid_to_cpu_present(m->mpc_apicid); } /* * Validate version @@ -202,6 +203,7 @@ void __init MP_processor_info (struct mpc_config_processor *m) ver = 0x10; } apic_version[m->mpc_apicid] = ver; + raw_phys_apicid[num_processors - 1] = m->mpc_apicid; } static void __init MP_bus_info (struct mpc_config_bus *m) diff --git a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c index 9d513dc1ceb2..acc6d8e48075 100644 --- a/arch/i386/kernel/smpboot.c +++ b/arch/i386/kernel/smpboot.c @@ -51,6 +51,7 @@ #include #include #include "smpboot_hooks.h" +#include "mach_apic.h" /* Set if we find a B stepping CPU */ static int __initdata smp_b_stepping; diff --git a/arch/i386/mach-generic/mach_apic.h b/arch/i386/mach-generic/mach_apic.h index 6b3dd2e232cc..f7be859e761e 100644 --- a/arch/i386/mach-generic/mach_apic.h +++ b/arch/i386/mach-generic/mach_apic.h @@ -30,4 +30,17 @@ static inline void clustered_apic_check(void) (clustered_apic_mode ? "NUMA-Q" : "Flat"), nr_ioapics); } +static inline int cpu_present_to_apicid(int mps_cpu) +{ + if (clustered_apic_mode) + return ( ((mps_cpu/4)*16) + (1<<(mps_cpu%4)) ); + else + return mps_cpu; +} + +static inline unsigned long apicid_to_cpu_present(int apicid) +{ + return (1ul << apicid); +} + #endif /* __ASM_MACH_APIC_H */ diff --git a/arch/i386/mach-summit/mach_apic.h b/arch/i386/mach-summit/mach_apic.h index 0c21ed845c1a..4cc36cd80092 100644 --- a/arch/i386/mach-summit/mach_apic.h +++ b/arch/i386/mach-summit/mach_apic.h @@ -38,4 +38,20 @@ static inline void clustered_apic_check(void) (x86_summit ? "Summit" : "Flat"), nr_ioapics); } +static inline int cpu_present_to_apicid(int mps_cpu) +{ + if (x86_summit) + return (int) raw_phys_apicid[mps_cpu]; + else + return mps_cpu; +} + +static inline unsigned long apicid_to_phys_cpu_present(int apicid) +{ + if (x86_summit) + return (1ul << (((apicid >> 4) << 2) | (apicid & 0x3))); + else + return (1ul << apicid); +} + #endif /* __ASM_MACH_APIC_H */ diff --git a/include/asm-i386/mpspec.h b/include/asm-i386/mpspec.h index f2a73c118d33..6fee20b0ef9f 100644 --- a/include/asm-i386/mpspec.h +++ b/include/asm-i386/mpspec.h @@ -16,11 +16,11 @@ /* * a maximum of 16 APICs with the current APIC ID architecture. */ -#ifdef CONFIG_X86_NUMAQ +#ifdef CONFIG_X86_NUMA #define MAX_APICS 256 -#else /* !CONFIG_X86_NUMAQ */ +#else /* !CONFIG_X86_NUMA */ #define MAX_APICS 16 -#endif /* CONFIG_X86_NUMAQ */ +#endif /* CONFIG_X86_NUMA */ #define MAX_MPC_ENTRY 1024 diff --git a/include/asm-i386/smp.h b/include/asm-i386/smp.h index a0d552e5d843..6df7592b84f5 100644 --- a/include/asm-i386/smp.h +++ b/include/asm-i386/smp.h @@ -65,6 +65,7 @@ extern void zap_low_mappings (void); * the real APIC ID <-> CPU # mapping. */ #define MAX_APICID 256 +#define BAD_APICID 0xFFu extern volatile int cpu_to_physical_apicid[NR_CPUS]; extern volatile int physical_apicid_to_cpu[MAX_APICID]; extern volatile int cpu_to_logical_apicid[NR_CPUS]; diff --git a/include/asm-i386/smpboot.h b/include/asm-i386/smpboot.h index 6a77a1663ca9..a3840aa6eca8 100644 --- a/include/asm-i386/smpboot.h +++ b/include/asm-i386/smpboot.h @@ -23,15 +23,6 @@ #define boot_cpu_apicid boot_cpu_physical_apicid #endif /* CONFIG_CLUSTERED_APIC */ -/* - * How to map from the cpu_present_map - */ -#ifdef CONFIG_CLUSTERED_APIC - #define cpu_present_to_apicid(mps_cpu) ( ((mps_cpu/4)*16) + (1<<(mps_cpu%4)) ) -#else /* !CONFIG_CLUSTERED_APIC */ - #define cpu_present_to_apicid(apicid) (apicid) -#endif /* CONFIG_CLUSTERED_APIC */ - /* * Mappings between logical cpu number and logical / physical apicid * The first four macros are trivial, but it keeps the abstraction consistent -- cgit v1.2.3 From 1477a825d7e6486a077608c7baf6abbb6f27ed95 Mon Sep 17 00:00:00 2001 From: Dipankar Sarma Date: Tue, 15 Oct 2002 05:40:46 -0700 Subject: [PATCH] Read-Copy Update infrastructure This is the RCU core patch from akpm's tree. It has been in his tree since about 2.5.37-mm1 along with dcache_rcu and so far it has worked fine. For 2.5, I am hoping that we might get the following RCU patches included - 1. rt_rcu - ipv4 routecache lookup. Davem agreed to include this patch if and when you include RCU core in your tree. 2. dcache_rcu (by Maneesh Soni) - dcache lookup avoiding dcache_lock as much as possible. This has been akpm's tree - stable and gives us good yield. I have been submitting this to Viro and I will publish some more benchmark numbers later to help decide on this. This RCU core implements RCU APIs, call_rcu() and synchronize_kernel(), by monitoring a per-CPU quiescent state (idle/user etc.) counter. call_rcu() queues a callback to be invoked after all the CPUs have gone through a quiescent state. Queuing is per-CPU and each per-CPU batch gets a batch number. As batches get their turn, a global cpu mask is used to keep track of CPUs pending quiescent state. Checking for quiescent cycle is done by saving the per-CPU counter at the beginning of the batch and then monitoring it for change through the local timer interrupt handler. --- include/linux/rcupdate.h | 134 ++++++++++++++++++++++++++ init/main.c | 2 + kernel/Makefile | 5 +- kernel/rcupdate.c | 242 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/sched.c | 5 + 5 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 include/linux/rcupdate.h create mode 100644 kernel/rcupdate.c (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h new file mode 100644 index 000000000000..a5ffb7bb5743 --- /dev/null +++ b/include/linux/rcupdate.h @@ -0,0 +1,134 @@ +/* + * Read-Copy Update mechanism for mutual exclusion + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (c) IBM Corporation, 2001 + * + * Author: Dipankar Sarma + * + * Based on the original work by Paul McKenney + * and inputs from Rusty Russell, Andrea Arcangeli and Andi Kleen. + * Papers: + * http://www.rdrop.com/users/paulmck/paper/rclockpdcsproof.pdf + * http://lse.sourceforge.net/locking/rclock_OLS.2001.05.01c.sc.pdf (OLS2001) + * + * For detailed explanation of Read-Copy Update mechanism see - + * http://lse.sourceforge.net/locking/rcupdate.html + * + */ + +#ifndef __LINUX_RCUPDATE_H +#define __LINUX_RCUPDATE_H + +#ifdef __KERNEL__ + +#include +#include +#include +#include + +/** + * struct rcu_head - callback structure for use with RCU + * @list: list_head to queue the update requests + * @func: actual update function to call after the grace period. + * @arg: argument to be passed to the actual update function. + */ +struct rcu_head { + struct list_head list; + void (*func)(void *obj); + void *arg; +}; + +#define RCU_HEAD_INIT(head) \ + { list: LIST_HEAD_INIT(head.list), func: NULL, arg: NULL } +#define RCU_HEAD(head) struct rcu_head head = RCU_HEAD_INIT(head) +#define INIT_RCU_HEAD(ptr) do { \ + INIT_LIST_HEAD(&(ptr)->list); (ptr)->func = NULL; (ptr)->arg = NULL; \ +} while (0) + + + +/* Control variables for rcupdate callback mechanism. */ +struct rcu_ctrlblk { + spinlock_t mutex; /* Guard this struct */ + long curbatch; /* Current batch number. */ + long maxbatch; /* Max requested batch number. */ + unsigned long rcu_cpu_mask; /* CPUs that need to switch in order */ + /* for current batch to proceed. */ +}; + +/* Is batch a before batch b ? */ +static inline int rcu_batch_before(long a, long b) +{ + return (a - b) < 0; +} + +/* Is batch a after batch b ? */ +static inline int rcu_batch_after(long a, long b) +{ + return (a - b) > 0; +} + +/* + * Per-CPU data for Read-Copy UPdate. + * nxtlist - new callbacks are added here + * curlist - current batch for which quiescent cycle started if any + */ +struct rcu_data { + long qsctr; /* User-mode/idle loop etc. */ + long last_qsctr; /* value of qsctr at beginning */ + /* of rcu grace period */ + long batch; /* Batch # for current RCU batch */ + struct list_head nxtlist; + struct list_head curlist; +} ____cacheline_aligned_in_smp; + +extern struct rcu_data rcu_data[NR_CPUS]; +extern struct rcu_ctrlblk rcu_ctrlblk; + +#define RCU_qsctr(cpu) (rcu_data[(cpu)].qsctr) +#define RCU_last_qsctr(cpu) (rcu_data[(cpu)].last_qsctr) +#define RCU_batch(cpu) (rcu_data[(cpu)].batch) +#define RCU_nxtlist(cpu) (rcu_data[(cpu)].nxtlist) +#define RCU_curlist(cpu) (rcu_data[(cpu)].curlist) + +#define RCU_QSCTR_INVALID 0 + +static inline int rcu_pending(int cpu) +{ + if ((!list_empty(&RCU_curlist(cpu)) && + rcu_batch_before(RCU_batch(cpu), rcu_ctrlblk.curbatch)) || + (list_empty(&RCU_curlist(cpu)) && + !list_empty(&RCU_nxtlist(cpu))) || + test_bit(cpu, &rcu_ctrlblk.rcu_cpu_mask)) + return 1; + else + return 0; +} + +#define rcu_read_lock() preempt_disable() +#define rcu_read_unlock() preempt_enable() + +extern void rcu_init(void); +extern void rcu_check_callbacks(int cpu, int user); + +/* Exported interfaces */ +extern void FASTCALL(call_rcu(struct rcu_head *head, + void (*func)(void *arg), void *arg)); +extern void synchronize_kernel(void); + +#endif /* __KERNEL__ */ +#endif /* __LINUX_RCUPDATE_H */ diff --git a/init/main.c b/init/main.c index f69c298b9a6f..e94bba3f7f44 100644 --- a/init/main.c +++ b/init/main.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -398,6 +399,7 @@ asmlinkage void __init start_kernel(void) printk("Kernel command line: %s\n", saved_command_line); parse_options(command_line); trap_init(); + rcu_init(); init_IRQ(); sched_init(); softirq_init(); diff --git a/kernel/Makefile b/kernel/Makefile index b3fce6d3ac9c..f862dedfabe9 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -3,12 +3,13 @@ # export-objs = signal.o sys.o kmod.o workqueue.o ksyms.o pm.o exec_domain.o \ - printk.o platform.o suspend.o dma.o module.o cpufreq.o + printk.o platform.o suspend.o dma.o module.o cpufreq.o rcupdate.o obj-y = sched.o fork.o exec_domain.o panic.o printk.o \ module.o exit.o itimer.o time.o softirq.o resource.o \ sysctl.o capability.o ptrace.o timer.o user.o \ - signal.o sys.o kmod.o workqueue.o futex.o platform.o pid.o + signal.o sys.o kmod.o workqueue.o futex.o platform.o pid.o \ + rcupdate.o obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o obj-$(CONFIG_SMP) += cpu.o diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c new file mode 100644 index 000000000000..dfdf1774489d --- /dev/null +++ b/kernel/rcupdate.c @@ -0,0 +1,242 @@ +/* + * Read-Copy Update mechanism for mutual exclusion + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (c) IBM Corporation, 2001 + * + * Author: Dipankar Sarma + * + * Based on the original work by Paul McKenney + * and inputs from Rusty Russell, Andrea Arcangeli and Andi Kleen. + * Papers: + * http://www.rdrop.com/users/paulmck/paper/rclockpdcsproof.pdf + * http://lse.sourceforge.net/locking/rclock_OLS.2001.05.01c.sc.pdf (OLS2001) + * + * For detailed explanation of Read-Copy Update mechanism see - + * http://lse.sourceforge.net/locking/rcupdate.html + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Definition for rcupdate control block. */ +struct rcu_ctrlblk rcu_ctrlblk = + { .mutex = SPIN_LOCK_UNLOCKED, .curbatch = 1, + .maxbatch = 1, .rcu_cpu_mask = 0 }; +struct rcu_data rcu_data[NR_CPUS] __cacheline_aligned; + +/* Fake initialization required by compiler */ +static DEFINE_PER_CPU(struct tasklet_struct, rcu_tasklet) = {NULL}; +#define RCU_tasklet(cpu) (per_cpu(rcu_tasklet, cpu)) + +/** + * call_rcu - Queue an RCU update request. + * @head: structure to be used for queueing the RCU updates. + * @func: actual update function to be invoked after the grace period + * @arg: argument to be passed to the update function + * + * The update function will be invoked as soon as all CPUs have performed + * a context switch or been seen in the idle loop or in a user process. + * The read-side of critical section that use call_rcu() for updation must + * be protected by rcu_read_lock()/rcu_read_unlock(). + */ +void call_rcu(struct rcu_head *head, void (*func)(void *arg), void *arg) +{ + int cpu; + unsigned long flags; + + head->func = func; + head->arg = arg; + local_irq_save(flags); + cpu = smp_processor_id(); + list_add_tail(&head->list, &RCU_nxtlist(cpu)); + local_irq_restore(flags); +} + +/* + * Invoke the completed RCU callbacks. They are expected to be in + * a per-cpu list. + */ +static void rcu_do_batch(struct list_head *list) +{ + struct list_head *entry; + struct rcu_head *head; + + while (!list_empty(list)) { + entry = list->next; + list_del(entry); + head = list_entry(entry, struct rcu_head, list); + head->func(head->arg); + } +} + +/* + * Register a new batch of callbacks, and start it up if there is currently no + * active batch and the batch to be registered has not already occurred. + * Caller must hold the rcu_ctrlblk lock. + */ +static void rcu_start_batch(long newbatch) +{ + if (rcu_batch_before(rcu_ctrlblk.maxbatch, newbatch)) { + rcu_ctrlblk.maxbatch = newbatch; + } + if (rcu_batch_before(rcu_ctrlblk.maxbatch, rcu_ctrlblk.curbatch) || + (rcu_ctrlblk.rcu_cpu_mask != 0)) { + return; + } + rcu_ctrlblk.rcu_cpu_mask = cpu_online_map; +} + +/* + * Check if the cpu has gone through a quiescent state (say context + * switch). If so and if it already hasn't done so in this RCU + * quiescent cycle, then indicate that it has done so. + */ +static void rcu_check_quiescent_state(void) +{ + int cpu = smp_processor_id(); + + if (!test_bit(cpu, &rcu_ctrlblk.rcu_cpu_mask)) { + return; + } + + /* + * Races with local timer interrupt - in the worst case + * we may miss one quiescent state of that CPU. That is + * tolerable. So no need to disable interrupts. + */ + if (RCU_last_qsctr(cpu) == RCU_QSCTR_INVALID) { + RCU_last_qsctr(cpu) = RCU_qsctr(cpu); + return; + } + if (RCU_qsctr(cpu) == RCU_last_qsctr(cpu)) { + return; + } + + spin_lock(&rcu_ctrlblk.mutex); + if (!test_bit(cpu, &rcu_ctrlblk.rcu_cpu_mask)) { + spin_unlock(&rcu_ctrlblk.mutex); + return; + } + clear_bit(cpu, &rcu_ctrlblk.rcu_cpu_mask); + RCU_last_qsctr(cpu) = RCU_QSCTR_INVALID; + if (rcu_ctrlblk.rcu_cpu_mask != 0) { + spin_unlock(&rcu_ctrlblk.mutex); + return; + } + rcu_ctrlblk.curbatch++; + rcu_start_batch(rcu_ctrlblk.maxbatch); + spin_unlock(&rcu_ctrlblk.mutex); +} + + +/* + * This does the RCU processing work from tasklet context. + */ +static void rcu_process_callbacks(unsigned long unused) +{ + int cpu = smp_processor_id(); + LIST_HEAD(list); + + if (!list_empty(&RCU_curlist(cpu)) && + rcu_batch_after(rcu_ctrlblk.curbatch, RCU_batch(cpu))) { + list_splice(&RCU_curlist(cpu), &list); + INIT_LIST_HEAD(&RCU_curlist(cpu)); + } + + local_irq_disable(); + if (!list_empty(&RCU_nxtlist(cpu)) && list_empty(&RCU_curlist(cpu))) { + list_splice(&RCU_nxtlist(cpu), &RCU_curlist(cpu)); + INIT_LIST_HEAD(&RCU_nxtlist(cpu)); + local_irq_enable(); + + /* + * start the next batch of callbacks + */ + spin_lock(&rcu_ctrlblk.mutex); + RCU_batch(cpu) = rcu_ctrlblk.curbatch + 1; + rcu_start_batch(RCU_batch(cpu)); + spin_unlock(&rcu_ctrlblk.mutex); + } else { + local_irq_enable(); + } + rcu_check_quiescent_state(); + if (!list_empty(&list)) + rcu_do_batch(&list); +} + +void rcu_check_callbacks(int cpu, int user) +{ + if (user || + (idle_cpu(cpu) && !in_softirq() && hardirq_count() <= 1)) + RCU_qsctr(cpu)++; + tasklet_schedule(&RCU_tasklet(cpu)); +} + +/* + * Initializes rcu mechanism. Assumed to be called early. + * That is before local timer(SMP) or jiffie timer (uniproc) is setup. + * Note that rcu_qsctr and friends are implicitly + * initialized due to the choice of ``0'' for RCU_CTR_INVALID. + */ +void __init rcu_init(void) +{ + int i; + + memset(&rcu_data[0], 0, sizeof(rcu_data)); + for (i = 0; i < NR_CPUS; i++) { + tasklet_init(&RCU_tasklet(i), rcu_process_callbacks, 0UL); + INIT_LIST_HEAD(&RCU_nxtlist(i)); + INIT_LIST_HEAD(&RCU_curlist(i)); + } +} + +/* Because of FASTCALL declaration of complete, we use this wrapper */ +static void wakeme_after_rcu(void *completion) +{ + complete(completion); +} + +/** + * synchronize-kernel - wait until all the CPUs have gone + * through a "quiescent" state. It may sleep. + */ +void synchronize_kernel(void) +{ + struct rcu_head rcu; + DECLARE_COMPLETION(completion); + + /* Will wake me after RCU finished */ + call_rcu(&rcu, wakeme_after_rcu, &completion); + + /* Wait for it */ + wait_for_completion(&completion); +} + + +EXPORT_SYMBOL(call_rcu); +EXPORT_SYMBOL(synchronize_kernel); diff --git a/kernel/sched.c b/kernel/sched.c index 0464ac0649b8..20d2854c0bc6 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -31,6 +31,7 @@ #include #include #include +#include /* * Convert user-nice values [ -20 ... 0 ... 19 ] @@ -865,6 +866,9 @@ void scheduler_tick(int user_ticks, int sys_ticks) runqueue_t *rq = this_rq(); task_t *p = current; + if (rcu_pending(cpu)) + rcu_check_callbacks(cpu, user_ticks); + if (p == rq->idle) { /* note: this timer irq context must be accounted for as well */ if (irq_count() - HARDIRQ_OFFSET >= SOFTIRQ_OFFSET) @@ -1023,6 +1027,7 @@ pick_next_task: switch_tasks: prefetch(next); clear_tsk_need_resched(prev); + RCU_qsctr(prev->thread_info->cpu)++; if (likely(prev != next)) { rq->nr_switches++; -- cgit v1.2.3 From 3d970ece801f97bae78c06e473f5d951d37ca458 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 15 Oct 2002 05:47:29 -0700 Subject: [PATCH] AFS filesystem (1/2) This adds RxRPC support to Linux for use by the AFS filesystem --- include/rxrpc/call.h | 218 +++++ include/rxrpc/connection.h | 83 ++ include/rxrpc/krxiod.h | 27 + include/rxrpc/krxsecd.h | 22 + include/rxrpc/krxtimod.h | 45 + include/rxrpc/message.h | 72 ++ include/rxrpc/packet.h | 128 +++ include/rxrpc/peer.h | 80 ++ include/rxrpc/rxrpc.h | 29 + include/rxrpc/transport.h | 115 +++ include/rxrpc/types.h | 39 + net/Makefile | 1 + net/rxrpc/Makefile | 33 + net/rxrpc/call.c | 2122 ++++++++++++++++++++++++++++++++++++++++++++ net/rxrpc/connection.c | 687 ++++++++++++++ net/rxrpc/internal.h | 107 +++ net/rxrpc/krxiod.c | 262 ++++++ net/rxrpc/krxsecd.c | 278 ++++++ net/rxrpc/krxtimod.c | 210 +++++ net/rxrpc/main.c | 127 +++ net/rxrpc/peer.c | 380 ++++++++ net/rxrpc/proc.c | 612 +++++++++++++ net/rxrpc/rxrpc_syms.c | 51 ++ net/rxrpc/sysctl.c | 73 ++ net/rxrpc/transport.c | 824 +++++++++++++++++ 25 files changed, 6625 insertions(+) create mode 100644 include/rxrpc/call.h create mode 100644 include/rxrpc/connection.h create mode 100644 include/rxrpc/krxiod.h create mode 100644 include/rxrpc/krxsecd.h create mode 100644 include/rxrpc/krxtimod.h create mode 100644 include/rxrpc/message.h create mode 100644 include/rxrpc/packet.h create mode 100644 include/rxrpc/peer.h create mode 100644 include/rxrpc/rxrpc.h create mode 100644 include/rxrpc/transport.h create mode 100644 include/rxrpc/types.h create mode 100644 net/rxrpc/Makefile create mode 100644 net/rxrpc/call.c create mode 100644 net/rxrpc/connection.c create mode 100644 net/rxrpc/internal.h create mode 100644 net/rxrpc/krxiod.c create mode 100644 net/rxrpc/krxsecd.c create mode 100644 net/rxrpc/krxtimod.c create mode 100644 net/rxrpc/main.c create mode 100644 net/rxrpc/peer.c create mode 100644 net/rxrpc/proc.c create mode 100644 net/rxrpc/rxrpc_syms.c create mode 100644 net/rxrpc/sysctl.c create mode 100644 net/rxrpc/transport.c (limited to 'include') diff --git a/include/rxrpc/call.h b/include/rxrpc/call.h new file mode 100644 index 000000000000..0ae39ad9c612 --- /dev/null +++ b/include/rxrpc/call.h @@ -0,0 +1,218 @@ +/* call.h: Rx call record + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#ifndef _LINUX_RXRPC_CALL_H +#define _LINUX_RXRPC_CALL_H + +#include +#include +#include +#include + +#define RXRPC_CALL_ACK_WINDOW_SIZE 16 + +extern unsigned rxrpc_call_rcv_timeout; /* receive activity timeout (secs) */ +extern unsigned rxrpc_call_acks_timeout; /* pending ACK (retransmit) timeout (secs) */ +extern unsigned rxrpc_call_dfr_ack_timeout; /* deferred ACK timeout (secs) */ +extern unsigned short rxrpc_call_max_resend; /* maximum consecutive resend count */ + +/* application call state + * - only state 0 and ffff are reserved, the state is set to 1 after an opid is received + */ +enum rxrpc_app_cstate { + RXRPC_CSTATE_COMPLETE = 0, /* operation complete */ + RXRPC_CSTATE_ERROR, /* operation ICMP error or aborted */ + RXRPC_CSTATE_SRVR_RCV_OPID, /* [SERVER] receiving operation ID */ + RXRPC_CSTATE_SRVR_RCV_ARGS, /* [SERVER] receiving operation data */ + RXRPC_CSTATE_SRVR_GOT_ARGS, /* [SERVER] completely received operation data */ + RXRPC_CSTATE_SRVR_SND_REPLY, /* [SERVER] sending operation reply */ + RXRPC_CSTATE_SRVR_RCV_FINAL_ACK, /* [SERVER] receiving final ACK */ + RXRPC_CSTATE_CLNT_SND_ARGS, /* [CLIENT] sending operation args */ + RXRPC_CSTATE_CLNT_RCV_REPLY, /* [CLIENT] receiving operation reply */ + RXRPC_CSTATE_CLNT_GOT_REPLY, /* [CLIENT] completely received operation reply */ +} __attribute__((packed)); + +extern const char *rxrpc_call_states[]; + +enum rxrpc_app_estate { + RXRPC_ESTATE_NO_ERROR = 0, /* no error */ + RXRPC_ESTATE_LOCAL_ABORT, /* aborted locally by application layer */ + RXRPC_ESTATE_PEER_ABORT, /* aborted remotely by peer */ + RXRPC_ESTATE_LOCAL_ERROR, /* local ICMP network error */ + RXRPC_ESTATE_REMOTE_ERROR, /* remote ICMP network error */ +} __attribute__((packed)); + +extern const char *rxrpc_call_error_states[]; + +/*****************************************************************************/ +/* + * Rx call record and application scratch buffer + * - the call record occupies the bottom of a complete page + * - the application scratch buffer occupies the rest + */ +struct rxrpc_call +{ + atomic_t usage; + struct rxrpc_connection *conn; /* connection upon which active */ + spinlock_t lock; /* access lock */ + struct module *owner; /* owner module */ + wait_queue_head_t waitq; /* wait queue for events to happen */ + struct list_head link; /* general internal list link */ + struct list_head call_link; /* master call list link */ + u32 chan_ix; /* connection channel index (net order) */ + u32 call_id; /* call ID on connection (net order) */ + unsigned long cjif; /* jiffies at call creation */ + unsigned long flags; /* control flags */ +#define RXRPC_CALL_ACKS_TIMO 0x00000001 /* ACKS timeout reached */ +#define RXRPC_CALL_ACKR_TIMO 0x00000002 /* ACKR timeout reached */ +#define RXRPC_CALL_RCV_TIMO 0x00000004 /* RCV timeout reached */ +#define RXRPC_CALL_RCV_PKT 0x00000008 /* received packet */ + + /* transmission */ + rxrpc_seq_t snd_seq_count; /* outgoing packet sequence number counter */ + struct rxrpc_message *snd_nextmsg; /* next message being constructed for sending */ + struct rxrpc_message *snd_ping; /* last ping message sent */ + unsigned short snd_resend_cnt; /* count of resends since last ACK */ + + /* transmission ACK tracking */ + struct list_head acks_pendq; /* messages pending ACK (ordered by seq) */ + unsigned acks_pend_cnt; /* number of un-ACK'd packets */ + rxrpc_seq_t acks_dftv_seq; /* highest definitively ACK'd msg seq */ + struct timer_list acks_timeout; /* timeout on expected ACK */ + + /* reception */ + struct list_head rcv_receiveq; /* messages pending reception (ordered by seq) */ + struct list_head rcv_krxiodq_lk; /* krxiod queue for new inbound packets */ + struct timer_list rcv_timeout; /* call receive activity timeout */ + + /* reception ACK'ing */ + rxrpc_seq_t ackr_win_bot; /* bottom of ACK window */ + rxrpc_seq_t ackr_win_top; /* top of ACK window */ + rxrpc_seq_t ackr_high_seq; /* highest seqno yet received */ + rxrpc_seq_t ackr_prev_seq; /* previous seqno received */ + unsigned ackr_pend_cnt; /* number of pending ACKs */ + struct timer_list ackr_dfr_timo; /* timeout on deferred ACK */ + char ackr_dfr_perm; /* request for deferred ACKs permitted */ + rxrpc_seq_t ackr_dfr_seq; /* seqno for deferred ACK */ + struct rxrpc_ackpacket ackr; /* pending normal ACK packet */ + u8 ackr_array[RXRPC_CALL_ACK_WINDOW_SIZE]; /* ACK records */ + + /* presentation layer */ + char app_last_rcv; /* T if received last packet from remote end */ + enum rxrpc_app_cstate app_call_state; /* call state */ + enum rxrpc_app_estate app_err_state; /* abort/error state */ + struct list_head app_readyq; /* ordered ready received packet queue */ + struct list_head app_unreadyq; /* ordered post-hole recv'd packet queue */ + rxrpc_seq_t app_ready_seq; /* last seq number dropped into readyq */ + size_t app_ready_qty; /* amount of data ready in readyq */ + unsigned app_opcode; /* operation ID */ + unsigned app_abort_code; /* abort code (when aborted) */ + int app_errno; /* error number (when ICMP error received) */ + + /* statisics */ + unsigned pkt_rcv_count; /* count of received packets on this call */ + unsigned pkt_snd_count; /* count of sent packets on this call */ + unsigned app_read_count; /* number of reads issued */ + + /* bits for the application to use */ + rxrpc_call_attn_func_t app_attn_func; /* callback when attention required */ + rxrpc_call_error_func_t app_error_func; /* callback when abort sent (cleanup and put) */ + rxrpc_call_aemap_func_t app_aemap_func; /* callback to map abort code to/from errno */ + void *app_user; /* application data */ + struct list_head app_link; /* application list linkage */ + struct list_head app_attn_link; /* application attention list linkage */ + size_t app_mark; /* trigger callback when app_ready_qty>=app_mark */ + char app_async_read; /* T if in async-read mode */ + u8 *app_read_buf; /* application async read buffer (app_mark size) */ + u8 *app_scr_alloc; /* application scratch allocation pointer */ + void *app_scr_ptr; /* application pointer into scratch buffer */ + +#define RXRPC_APP_MARK_EOF 0xFFFFFFFFU /* mark at end of input */ + + /* application scratch buffer */ + u8 app_scratch[0] __attribute__((aligned(sizeof(long)))); +}; + +#define RXRPC_CALL_SCRATCH_SIZE (PAGE_SIZE - sizeof(struct rxrpc_call)) + +#define rxrpc_call_reset_scratch(CALL) \ +do { (CALL)->app_scr_alloc = (CALL)->app_scratch; } while(0) + +#define rxrpc_call_alloc_scratch(CALL,SIZE) \ +({ \ + void *ptr; \ + ptr = (CALL)->app_scr_alloc; \ + (CALL)->app_scr_alloc += (SIZE); \ + if ((SIZE)>RXRPC_CALL_SCRATCH_SIZE || \ + (size_t)((CALL)->app_scr_alloc - (u8*)(CALL)) > RXRPC_CALL_SCRATCH_SIZE) { \ + printk("rxrpc_call_alloc_scratch(%p,%u)\n",(CALL),(SIZE)); \ + BUG(); \ + } \ + ptr; \ +}) + +#define rxrpc_call_alloc_scratch_s(CALL,TYPE) \ +({ \ + size_t size = sizeof(TYPE); \ + TYPE *ptr; \ + ptr = (TYPE*)(CALL)->app_scr_alloc; \ + (CALL)->app_scr_alloc += size; \ + if (size>RXRPC_CALL_SCRATCH_SIZE || \ + (size_t)((CALL)->app_scr_alloc - (u8*)(CALL)) > RXRPC_CALL_SCRATCH_SIZE) { \ + printk("rxrpc_call_alloc_scratch(%p,%u)\n",(CALL),size); \ + BUG(); \ + } \ + ptr; \ +}) + +#define rxrpc_call_is_ack_pending(CALL) ((CALL)->ackr.reason != 0) + +extern int rxrpc_create_call(struct rxrpc_connection *conn, + rxrpc_call_attn_func_t attn, + rxrpc_call_error_func_t error, + rxrpc_call_aemap_func_t aemap, + struct rxrpc_call **_call); + +extern int rxrpc_incoming_call(struct rxrpc_connection *conn, + struct rxrpc_message *msg, + struct rxrpc_call **_call); + +static inline void rxrpc_get_call(struct rxrpc_call *call) +{ + if (atomic_read(&call->usage)<=0) + BUG(); + atomic_inc(&call->usage); + /*printk("rxrpc_get_call(%p{u=%d})\n",(C),atomic_read(&(C)->usage));*/ +} + +extern void rxrpc_put_call(struct rxrpc_call *call); + +extern void rxrpc_call_do_stuff(struct rxrpc_call *call); + +extern int rxrpc_call_abort(struct rxrpc_call *call, int error); + +#define RXRPC_CALL_READ_BLOCK 0x0001 /* block if not enough data and not yet EOF */ +#define RXRPC_CALL_READ_ALL 0x0002 /* error if insufficient data received */ +extern int rxrpc_call_read_data(struct rxrpc_call *call, void *buffer, size_t size, int flags); + +extern int rxrpc_call_write_data(struct rxrpc_call *call, + size_t sioc, + struct iovec siov[], + u8 rxhdr_flags, + int alloc_flags, + int dup_data, + size_t *size_sent); + +extern int rxrpc_call_flush(struct rxrpc_call *call); + +extern void rxrpc_call_handle_error(struct rxrpc_call *conn, int local, int errno); + +#endif /* _LINUX_RXRPC_CALL_H */ diff --git a/include/rxrpc/connection.h b/include/rxrpc/connection.h new file mode 100644 index 000000000000..fc10fed01b21 --- /dev/null +++ b/include/rxrpc/connection.h @@ -0,0 +1,83 @@ +/* connection.h: Rx connection record + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#ifndef _LINUX_RXRPC_CONNECTION_H +#define _LINUX_RXRPC_CONNECTION_H + +#include +#include + +struct sk_buff; + +/*****************************************************************************/ +/* + * Rx connection + * - connections are matched by (rmt_port,rmt_addr,service_id,conn_id,clientflag) + * - connections only retain a refcount on the peer when they are active + * - connections with refcount==0 are inactive and reside in the peer's graveyard + */ +struct rxrpc_connection +{ + atomic_t usage; + struct rxrpc_transport *trans; /* transport endpoint */ + struct rxrpc_peer *peer; /* peer from/to which connected */ + struct rxrpc_service *service; /* responsible service (inbound conns) */ + struct rxrpc_timer timeout; /* decaching timer */ + struct list_head link; /* link in peer's list */ + struct list_head proc_link; /* link in proc list */ + struct list_head err_link; /* link in ICMP error processing list */ + struct sockaddr_in addr; /* remote address */ + struct rxrpc_call *channels[4]; /* channels (active calls) */ + wait_queue_head_t chanwait; /* wait for channel to become available */ + spinlock_t lock; /* access lock */ + struct timeval atime; /* last access time */ + size_t mtu_size; /* MTU size for outbound messages */ + unsigned call_counter; /* call ID counter */ + rxrpc_serial_t serial_counter; /* packet serial number counter */ + + /* the following should all be in net order */ + u32 in_epoch; /* peer's epoch */ + u32 out_epoch; /* my epoch */ + u32 conn_id; /* connection ID, appropriately shifted */ + u16 service_id; /* service ID */ + u8 security_ix; /* security ID */ + u8 in_clientflag; /* RXRPC_CLIENT_INITIATED if we are server */ + u8 out_clientflag; /* RXRPC_CLIENT_INITIATED if we are client */ +}; + +extern int rxrpc_create_connection(struct rxrpc_transport *trans, + u16 port, + u32 addr, + unsigned short service_id, + void *security, + struct rxrpc_connection **_conn); + +extern int rxrpc_connection_lookup(struct rxrpc_peer *peer, + struct rxrpc_message *msg, + struct rxrpc_connection **_conn); + +static inline void rxrpc_get_connection(struct rxrpc_connection *conn) +{ + if (atomic_read(&conn->usage)<0) + BUG(); + atomic_inc(&conn->usage); + //printk("rxrpc_get_conn(%p{u=%d})\n",conn,atomic_read(&conn->usage)); +} + +extern void rxrpc_put_connection(struct rxrpc_connection *conn); + +extern int rxrpc_conn_receive_call_packet(struct rxrpc_connection *conn, + struct rxrpc_call *call, + struct rxrpc_message *msg); + +extern void rxrpc_conn_handle_error(struct rxrpc_connection *conn, int local, int errno); + +#endif /* _LINUX_RXRPC_CONNECTION_H */ diff --git a/include/rxrpc/krxiod.h b/include/rxrpc/krxiod.h new file mode 100644 index 000000000000..c0e0e82e4df2 --- /dev/null +++ b/include/rxrpc/krxiod.h @@ -0,0 +1,27 @@ +/* krxiod.h: Rx RPC I/O kernel thread interface + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#ifndef _LINUX_RXRPC_KRXIOD_H +#define _LINUX_RXRPC_KRXIOD_H + +#include + +extern int rxrpc_krxiod_init(void); +extern void rxrpc_krxiod_kill(void); +extern void rxrpc_krxiod_queue_transport(struct rxrpc_transport *trans); +extern void rxrpc_krxiod_dequeue_transport(struct rxrpc_transport *trans); +extern void rxrpc_krxiod_queue_peer(struct rxrpc_peer *peer); +extern void rxrpc_krxiod_dequeue_peer(struct rxrpc_peer *peer); +extern void rxrpc_krxiod_clear_peers(struct rxrpc_transport *trans); +extern void rxrpc_krxiod_queue_call(struct rxrpc_call *call); +extern void rxrpc_krxiod_dequeue_call(struct rxrpc_call *call); + +#endif /* _LINUX_RXRPC_KRXIOD_H */ diff --git a/include/rxrpc/krxsecd.h b/include/rxrpc/krxsecd.h new file mode 100644 index 000000000000..55ce43a25b38 --- /dev/null +++ b/include/rxrpc/krxsecd.h @@ -0,0 +1,22 @@ +/* krxsecd.h: Rx RPC security kernel thread interface + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#ifndef _LINUX_RXRPC_KRXSECD_H +#define _LINUX_RXRPC_KRXSECD_H + +#include + +extern int rxrpc_krxsecd_init(void); +extern void rxrpc_krxsecd_kill(void); +extern void rxrpc_krxsecd_clear_transport(struct rxrpc_transport *trans); +extern void rxrpc_krxsecd_queue_incoming_call(struct rxrpc_message *msg); + +#endif /* _LINUX_RXRPC_KRXSECD_H */ diff --git a/include/rxrpc/krxtimod.h b/include/rxrpc/krxtimod.h new file mode 100644 index 000000000000..b3d298b612f2 --- /dev/null +++ b/include/rxrpc/krxtimod.h @@ -0,0 +1,45 @@ +/* krxtimod.h: RxRPC timeout daemon + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#ifndef _LINUX_RXRPC_KRXTIMOD_H +#define _LINUX_RXRPC_KRXTIMOD_H + +#include + +struct rxrpc_timer_ops { + /* called when the front of the timer queue has timed out */ + void (*timed_out)(struct rxrpc_timer *timer); +}; + +/*****************************************************************************/ +/* + * RXRPC timer/timeout record + */ +struct rxrpc_timer +{ + struct list_head link; /* link in timer queue */ + unsigned long timo_jif; /* timeout time */ + const struct rxrpc_timer_ops *ops; /* timeout expiry function */ +}; + +static inline void rxrpc_timer_init(rxrpc_timer_t *timer, const struct rxrpc_timer_ops *ops) +{ + INIT_LIST_HEAD(&timer->link); + timer->ops = ops; +} + +extern int rxrpc_krxtimod_start(void); +extern void rxrpc_krxtimod_kill(void); + +extern void rxrpc_krxtimod_add_timer(rxrpc_timer_t *timer, unsigned long timeout); +extern int rxrpc_krxtimod_del_timer(rxrpc_timer_t *timer); + +#endif /* _LINUX_RXRPC_KRXTIMOD_H */ diff --git a/include/rxrpc/message.h b/include/rxrpc/message.h new file mode 100644 index 000000000000..2e43c03c6857 --- /dev/null +++ b/include/rxrpc/message.h @@ -0,0 +1,72 @@ +/* message.h: Rx message caching + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#ifndef _H_3AD3363A_3A9C_11D6_83D8_0002B3163499 +#define _H_3AD3363A_3A9C_11D6_83D8_0002B3163499 + +#include + +/*****************************************************************************/ +/* + * Rx message record + */ +struct rxrpc_message +{ + atomic_t usage; + struct list_head link; /* list link */ + struct timeval stamp; /* time received or last sent */ + rxrpc_seq_t seq; /* message sequence number */ + + int state; /* the state the message is currently in */ +#define RXRPC_MSG_PREPARED 0 +#define RXRPC_MSG_SENT 1 +#define RXRPC_MSG_ACKED 2 /* provisionally ACK'd */ +#define RXRPC_MSG_DONE 3 /* definitively ACK'd (msg->sequsage); } while(0) + +extern void __rxrpc_put_message(struct rxrpc_message *msg); +static inline void rxrpc_put_message(struct rxrpc_message *msg) +{ + if (atomic_read(&msg->usage)<=0) + BUG(); + if (atomic_dec_and_test(&msg->usage)) + __rxrpc_put_message(msg); +} + +extern int rxrpc_conn_newmsg(struct rxrpc_connection *conn, + struct rxrpc_call *call, + u8 type, + int count, + struct iovec diov[], + int alloc_flags, + struct rxrpc_message **_msg); + +extern int rxrpc_conn_sendmsg(struct rxrpc_connection *conn, struct rxrpc_message *msg); + +#endif /* _H_3AD3363A_3A9C_11D6_83D8_0002B3163499 */ diff --git a/include/rxrpc/packet.h b/include/rxrpc/packet.h new file mode 100644 index 000000000000..78999077f5b8 --- /dev/null +++ b/include/rxrpc/packet.h @@ -0,0 +1,128 @@ +/* packet.h: Rx packet layout and definitions + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#ifndef _LINUX_RXRPC_PACKET_H +#define _LINUX_RXRPC_PACKET_H + +#include + +#define RXRPC_IPUDP_SIZE 28 +extern size_t RXRPC_MAX_PACKET_SIZE; +#define RXRPC_MAX_PACKET_DATA_SIZE (RXRPC_MAX_PACKET_SIZE - sizeof(struct rxrpc_header)) +#define RXRPC_LOCAL_PACKET_SIZE RXRPC_MAX_PACKET_SIZE +#define RXRPC_REMOTE_PACKET_SIZE (576 - RXRPC_IPUDP_SIZE) + +/*****************************************************************************/ +/* + * on-the-wire Rx packet header + * - all multibyte fields should be in network byte order + */ +struct rxrpc_header +{ + u32 epoch; /* client boot timestamp */ + + u32 cid; /* connection and channel ID */ +#define RXRPC_MAXCALLS 4 /* max active calls per conn */ +#define RXRPC_CHANNELMASK (RXRPC_MAXCALLS-1) /* mask for channel ID */ +#define RXRPC_CIDMASK (~RXRPC_CHANNELMASK) /* mask for connection ID */ +#define RXRPC_CIDSHIFT 2 /* shift for connection ID */ + + u32 callNumber; /* call ID (0 for connection-level packets) */ +#define RXRPC_PROCESS_MAXCALLS (1<<2) /* maximum number of active calls per conn (power of 2) */ + + u32 seq; /* sequence number of pkt in call stream */ + u32 serial; /* serial number of pkt sent to network */ + + u8 type; /* packet type */ +#define RXRPC_PACKET_TYPE_DATA 1 /* data */ +#define RXRPC_PACKET_TYPE_ACK 2 /* ACK */ +#define RXRPC_PACKET_TYPE_BUSY 3 /* call reject */ +#define RXRPC_PACKET_TYPE_ABORT 4 /* call/connection abort */ +#define RXRPC_PACKET_TYPE_ACKALL 5 /* ACK all outstanding packets on call */ +#define RXRPC_PACKET_TYPE_CHALLENGE 6 /* connection security challenge (SRVR->CLNT) */ +#define RXRPC_PACKET_TYPE_RESPONSE 7 /* connection secutity response (CLNT->SRVR) */ +#define RXRPC_PACKET_TYPE_DEBUG 8 /* debug info request */ +#define RXRPC_N_PACKET_TYPES 9 /* number of packet types (incl type 0) */ + + u8 flags; /* packet flags */ +#define RXRPC_CLIENT_INITIATED 0x01 /* signifies a packet generated by a client */ +#define RXRPC_REQUEST_ACK 0x02 /* request an unconditional ACK of this packet */ +#define RXRPC_LAST_PACKET 0x04 /* the last packet from this side for this call */ +#define RXRPC_MORE_PACKETS 0x08 /* more packets to come */ +#define RXRPC_JUMBO_PACKET 0x20 /* [DATA] this is a jumbo packet */ +#define RXRPC_SLOW_START_OK 0x20 /* [ACK] slow start supported */ + + u8 userStatus; /* app-layer defined status */ + u8 securityIndex; /* security protocol ID */ + u16 _rsvd; /* reserved (used by kerberos security as cksum) */ + u16 serviceId; /* service ID */ + +} __attribute__((packed)); + +#define __rxrpc_header_off(X) offsetof(struct rxrpc_header,X) + +extern const char *rxrpc_pkts[]; + +/*****************************************************************************/ +/* + * jumbo packet secondary header + * - can be mapped to read header by: + * - new_serial = serial + 1 + * - new_seq = seq + 1 + * - new_flags = j_flags + * - new__rsvd = j__rsvd + * - duplicating all other fields + */ +struct rxrpc_jumbo_header +{ + u8 flags; /* packet flags (as per rxrpc_header) */ + u8 pad; + u16 _rsvd; /* reserved (used by kerberos security as cksum) */ +}; + +#define RXRPC_JUMBO_DATALEN 1412 /* non-terminal jumbo packet data length */ + +/*****************************************************************************/ +/* + * on-the-wire Rx ACK packet data payload + * - all multibyte fields should be in network byte order + */ +struct rxrpc_ackpacket +{ + u16 bufferSpace; /* number of packet buffers available */ + u16 maxSkew; /* diff between serno being ACK'd and highest serial no received */ + u32 firstPacket; /* sequence no of first ACK'd packet in attached list */ + u32 previousPacket; /* sequence no of previous packet received */ + u32 serial; /* serial no of packet that prompted this ACK */ + + u8 reason; /* reason for ACK */ +#define RXRPC_ACK_REQUESTED 1 /* ACK was requested on packet */ +#define RXRPC_ACK_DUPLICATE 2 /* duplicate packet received */ +#define RXRPC_ACK_OUT_OF_SEQUENCE 3 /* out of sequence packet received */ +#define RXRPC_ACK_EXCEEDS_WINDOW 4 /* packet received beyond end of ACK window */ +#define RXRPC_ACK_NOSPACE 5 /* packet discarded due to lack of buffer space */ +#define RXRPC_ACK_PING 6 /* keep alive ACK */ +#define RXRPC_ACK_PING_RESPONSE 7 /* response to RXRPC_ACK_PING */ +#define RXRPC_ACK_DELAY 8 /* nothing happened since received packet */ +#define RXRPC_ACK_IDLE 9 /* ACK due to fully received ACK window */ + + u8 nAcks; /* number of ACKs */ +#define RXRPC_MAXACKS 255 + + u8 acks[0]; /* list of ACK/NAKs */ +#define RXRPC_ACK_TYPE_NACK 0 +#define RXRPC_ACK_TYPE_ACK 1 + +} __attribute__((packed)); + +extern const char *rxrpc_acks[]; + +#endif /* _LINUX_RXRPC_PACKET_H */ diff --git a/include/rxrpc/peer.h b/include/rxrpc/peer.h new file mode 100644 index 000000000000..9f09bc95a40f --- /dev/null +++ b/include/rxrpc/peer.h @@ -0,0 +1,80 @@ +/* peer.h: Rx RPC per-transport peer record + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#ifndef _LINUX_RXRPC_PEER_H +#define _LINUX_RXRPC_PEER_H + +#include +#include +#include + +struct rxrpc_peer_ops +{ + /* peer record being added */ + int (*adding)(struct rxrpc_peer *peer); + + /* peer record being discarded from graveyard */ + void (*discarding)(struct rxrpc_peer *peer); + + /* change of epoch detected on connection */ + void (*change_of_epoch)(struct rxrpc_connection *conn); +}; + +/*****************************************************************************/ +/* + * Rx RPC per-transport peer record + * - peers only retain a refcount on the transport when they are active + * - peers with refcount==0 are inactive and reside in the transport's graveyard + */ +struct rxrpc_peer +{ + atomic_t usage; + struct rxrpc_peer_ops *ops; /* operations on this peer */ + struct rxrpc_transport *trans; /* owner transport */ + struct rxrpc_timer timeout; /* timeout for grave destruction */ + struct list_head link; /* link in transport's peer list */ + struct list_head proc_link; /* link in /proc list */ + rwlock_t conn_lock; /* lock for connections */ + struct list_head conn_active; /* active connections to/from this peer */ + struct list_head conn_graveyard; /* graveyard for inactive connections */ + spinlock_t conn_gylock; /* lock for conn_graveyard */ + wait_queue_head_t conn_gy_waitq; /* wait queue hit when graveyard is empty */ + atomic_t conn_count; /* number of attached connections */ + struct in_addr addr; /* remote address */ + size_t if_mtu; /* interface MTU for this peer */ + spinlock_t lock; /* access lock */ + + void *user; /* application layer data */ + + /* calculated RTT cache */ +#define RXRPC_RTT_CACHE_SIZE 32 + suseconds_t rtt; /* current RTT estimate (in uS) */ + unsigned short rtt_point; /* next entry at which to insert */ + unsigned short rtt_usage; /* amount of cache actually used */ + suseconds_t rtt_cache[RXRPC_RTT_CACHE_SIZE]; /* calculated RTT cache */ +}; + + +extern int rxrpc_peer_lookup(struct rxrpc_transport *trans, + u32 addr, + struct rxrpc_peer **_peer); + +static inline void rxrpc_get_peer(struct rxrpc_peer *peer) +{ + if (atomic_read(&peer->usage)<0) + BUG(); + atomic_inc(&peer->usage); + //printk("rxrpc_get_peer(%p{u=%d})\n",peer,atomic_read(&peer->usage)); +} + +extern void rxrpc_put_peer(struct rxrpc_peer *peer); + +#endif /* _LINUX_RXRPC_PEER_H */ diff --git a/include/rxrpc/rxrpc.h b/include/rxrpc/rxrpc.h new file mode 100644 index 000000000000..454d59933675 --- /dev/null +++ b/include/rxrpc/rxrpc.h @@ -0,0 +1,29 @@ +/* rx.h: Rx RPC interface + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#ifndef _LINUX_RXRPC_RXRPC_H +#define _LINUX_RXRPC_RXRPC_H + +#ifdef __KERNEL__ + +extern u32 rxrpc_epoch; + +extern int rxrpc_ktrace; +extern int rxrpc_kdebug; +extern int rxrpc_kproto; +extern int rxrpc_knet; + +extern int rxrpc_sysctl_init(void); +extern void rxrpc_sysctl_cleanup(void); + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_RXRPC_RXRPC_H */ diff --git a/include/rxrpc/transport.h b/include/rxrpc/transport.h new file mode 100644 index 000000000000..b9c225533158 --- /dev/null +++ b/include/rxrpc/transport.h @@ -0,0 +1,115 @@ +/* transport.h: Rx transport management + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#ifndef _LINUX_RXRPC_TRANSPORT_H +#define _LINUX_RXRPC_TRANSPORT_H + +#include +#include +#include +#include +#include + +typedef int (*rxrpc_newcall_fnx_t)(struct rxrpc_call *call); + +extern wait_queue_head_t rxrpc_krxiod_wq; + +/*****************************************************************************/ +/* + * Rx operation specification + * - tables of these must be sorted by op ID so that they can be binary-chop searched + */ +struct rxrpc_operation +{ + unsigned id; /* operation ID */ + size_t asize; /* minimum size of argument block */ + const char *name; /* name of operation */ + void *user; /* initial user data */ +}; + +/*****************************************************************************/ +/* + * Rx transport service record + */ +struct rxrpc_service +{ + struct list_head link; /* link in services list on transport */ + struct module *owner; /* owner module */ + rxrpc_newcall_fnx_t new_call; /* new call handler function */ + const char *name; /* name of service */ + unsigned short service_id; /* Rx service ID */ + rxrpc_call_attn_func_t attn_func; /* call requires attention callback */ + rxrpc_call_error_func_t error_func; /* call error callback */ + rxrpc_call_aemap_func_t aemap_func; /* abort -> errno mapping callback */ + + const struct rxrpc_operation *ops_begin; /* beginning of operations table */ + const struct rxrpc_operation *ops_end; /* end of operations table */ +}; + +/*****************************************************************************/ +/* + * Rx transport endpoint record + */ +struct rxrpc_transport +{ + atomic_t usage; + struct socket *socket; /* my UDP socket */ + struct list_head services; /* services listening on this socket */ + struct list_head link; /* link in transport list */ + struct list_head proc_link; /* link in transport proc list */ + struct list_head krxiodq_link; /* krxiod attention queue link */ + spinlock_t lock; /* access lock */ + struct list_head peer_active; /* active peers connected to over this socket */ + struct list_head peer_graveyard; /* inactive peer list */ + spinlock_t peer_gylock; /* peer graveyard lock */ + wait_queue_head_t peer_gy_waitq; /* wait queue hit when peer graveyard is empty */ + rwlock_t peer_lock; /* peer list access lock */ + atomic_t peer_count; /* number of peers */ + struct rxrpc_peer_ops *peer_ops; /* default peer operations */ + unsigned short port; /* port upon which listening */ + volatile char error_rcvd; /* T if received ICMP error outstanding */ +}; + +extern struct list_head rxrpc_transports; + +extern int rxrpc_create_transport(unsigned short port, + struct rxrpc_transport **_trans); + +static inline void rxrpc_get_transport(struct rxrpc_transport *trans) +{ + if (atomic_read(&trans->usage)<=0) + BUG(); + atomic_inc(&trans->usage); + //printk("rxrpc_get_transport(%p{u=%d})\n",trans,atomic_read(&trans->usage)); +} + +extern void rxrpc_put_transport(struct rxrpc_transport *trans); + +extern int rxrpc_add_service(struct rxrpc_transport *trans, + struct rxrpc_service *srv); + +extern void rxrpc_del_service(struct rxrpc_transport *trans, + struct rxrpc_service *srv); + +#if 0 +extern int rxrpc_trans_add_connection(struct rxrpc_transport *trans, + struct rxrpc_connection *conn); +#endif + +extern void rxrpc_trans_receive_packet(struct rxrpc_transport *trans); + +extern int rxrpc_trans_immediate_abort(struct rxrpc_transport *trans, + struct rxrpc_message *msg, + int error); + +extern void rxrpc_clear_transport(struct rxrpc_transport *trans); + +#endif /* _LINUX_RXRPC_TRANSPORT_H */ diff --git a/include/rxrpc/types.h b/include/rxrpc/types.h new file mode 100644 index 000000000000..40700bc61a6f --- /dev/null +++ b/include/rxrpc/types.h @@ -0,0 +1,39 @@ +/* types.h: Rx types + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#ifndef _LINUX_RXRPC_TYPES_H +#define _LINUX_RXRPC_TYPES_H + +#include +#include +#include +#include +#include +#include + +typedef unsigned rxrpc_seq_t; /* Rx message sequence number */ +typedef unsigned rxrpc_serial_t; /* Rx message serial number */ + +struct rxrpc_call; +struct rxrpc_connection; +struct rxrpc_header; +struct rxrpc_message; +struct rxrpc_operation; +struct rxrpc_peer; +struct rxrpc_service; +typedef struct rxrpc_timer rxrpc_timer_t; +struct rxrpc_transport; + +typedef void (*rxrpc_call_attn_func_t)(struct rxrpc_call *call); +typedef void (*rxrpc_call_error_func_t)(struct rxrpc_call *call); +typedef void (*rxrpc_call_aemap_func_t)(struct rxrpc_call *call); + +#endif /* _LINUX_RXRPC_TYPES_H */ diff --git a/net/Makefile b/net/Makefile index 4d09f62752fa..f7e9d2b90fd0 100644 --- a/net/Makefile +++ b/net/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_AX25) += ax25/ obj-$(CONFIG_IRDA) += irda/ obj-$(CONFIG_BT) += bluetooth/ obj-$(CONFIG_SUNRPC) += sunrpc/ +obj-$(CONFIG_RXRPC) += rxrpc/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_DECNET) += decnet/ obj-$(CONFIG_ECONET) += econet/ diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile new file mode 100644 index 000000000000..63e3027738c4 --- /dev/null +++ b/net/rxrpc/Makefile @@ -0,0 +1,33 @@ +# +# Makefile for Linux kernel Rx RPC +# + +export-objs := rxrpc_syms.o + +rxrpc-objs := \ + call.o \ + connection.o \ + krxiod.o \ + krxsecd.o \ + krxtimod.o \ + main.o \ + peer.o \ + rxrpc_syms.o \ + transport.o + +#ifeq ($(CONFIG_PROC_FS),y) +rxrpc-objs += proc.o +#endif +#ifeq ($(CONFIG_SYSCTL),y) +rxrpc-objs += sysctl.o +#endif + +obj-m := rxrpc.o + +# superfluous for 2.5, but needed for 2.4.. +ifeq "$(VERSION).$(PATCHLEVEL)" "2.4" +rxrpc.o: $(rxrpc-objs) + $(LD) -r -o $@ $(rxrpc-objs) +endif + +include $(TOPDIR)/Rules.make diff --git a/net/rxrpc/call.c b/net/rxrpc/call.c new file mode 100644 index 000000000000..475fd925e5fa --- /dev/null +++ b/net/rxrpc/call.c @@ -0,0 +1,2122 @@ +/* call.c: Rx call routines + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +__RXACCT_DECL(atomic_t rxrpc_call_count); +__RXACCT_DECL(atomic_t rxrpc_message_count); + +LIST_HEAD(rxrpc_calls); +DECLARE_RWSEM(rxrpc_calls_sem); + +unsigned rxrpc_call_rcv_timeout = 30; +unsigned rxrpc_call_acks_timeout = 30; +unsigned rxrpc_call_dfr_ack_timeout = 5; +unsigned short rxrpc_call_max_resend = 10; + +const char *rxrpc_call_states[] = { + "COMPLETE", + "ERROR", + "SRVR_RCV_OPID", + "SRVR_RCV_ARGS", + "SRVR_GOT_ARGS", + "SRVR_SND_REPLY", + "SRVR_RCV_FINAL_ACK", + "CLNT_SND_ARGS", + "CLNT_RCV_REPLY", + "CLNT_GOT_REPLY" +}; + +const char *rxrpc_call_error_states[] = { + "NO_ERROR", + "LOCAL_ABORT", + "PEER_ABORT", + "LOCAL_ERROR", + "REMOTE_ERROR" +}; + +const char *rxrpc_pkts[] = { + "?00", "data", "ack", "busy", "abort", "ackall", "chall", "resp", "debug", + "?09", "?10", "?11", "?12", "?13", "?14", "?15" +}; + +const char *rxrpc_acks[] = { + "---", "REQ", "DUP", "SEQ", "WIN", "MEM", "PNG", "PNR", "DLY", "IDL", "-?-" +}; + +static const char _acktype[] = "NA-"; + +static void rxrpc_call_receive_packet(struct rxrpc_call *call); +static void rxrpc_call_receive_data_packet(struct rxrpc_call *call, struct rxrpc_message *msg); +static void rxrpc_call_receive_ack_packet(struct rxrpc_call *call, struct rxrpc_message *msg); +static void rxrpc_call_definitively_ACK(struct rxrpc_call *call, rxrpc_seq_t higest); +static void rxrpc_call_resend(struct rxrpc_call *call, rxrpc_seq_t highest); +static int __rxrpc_call_read_data(struct rxrpc_call *call); + +static int rxrpc_call_record_ACK(struct rxrpc_call *call, + struct rxrpc_message *msg, + rxrpc_seq_t seq, + size_t count); +#define _state(call) \ + _debug("[[[ state %s ]]]",rxrpc_call_states[call->app_call_state]); + +static void rxrpc_call_default_attn_func(struct rxrpc_call *call) +{ + wake_up(&call->waitq); +} + +static void rxrpc_call_default_error_func(struct rxrpc_call *call) +{ + wake_up(&call->waitq); +} + +static void rxrpc_call_default_aemap_func(struct rxrpc_call *call) +{ + switch (call->app_err_state) { + case RXRPC_ESTATE_LOCAL_ABORT: + call->app_abort_code = -call->app_errno; + case RXRPC_ESTATE_PEER_ABORT: + call->app_errno = -ECONNABORTED; + default: + break; + } +} + +static void __rxrpc_call_acks_timeout(unsigned long _call) +{ + struct rxrpc_call *call = (struct rxrpc_call *) _call; + + _debug("ACKS TIMEOUT %05lu",jiffies - call->cjif); + + call->flags |= RXRPC_CALL_ACKS_TIMO; + rxrpc_krxiod_queue_call(call); +} + +static void __rxrpc_call_rcv_timeout(unsigned long _call) +{ + struct rxrpc_call *call = (struct rxrpc_call *) _call; + + _debug("RCV TIMEOUT %05lu",jiffies - call->cjif); + + call->flags |= RXRPC_CALL_RCV_TIMO; + rxrpc_krxiod_queue_call(call); +} + +static void __rxrpc_call_ackr_timeout(unsigned long _call) +{ + struct rxrpc_call *call = (struct rxrpc_call *) _call; + + _debug("ACKR TIMEOUT %05lu",jiffies - call->cjif); + + call->flags |= RXRPC_CALL_ACKR_TIMO; + rxrpc_krxiod_queue_call(call); +} + +/*****************************************************************************/ +/* + * create a new call record + */ +static inline int __rxrpc_create_call(struct rxrpc_connection *conn, + struct rxrpc_call **_call) +{ + struct rxrpc_call *call; + + _enter("%p",conn); + + /* allocate and initialise a call record */ + call = (struct rxrpc_call *) get_zeroed_page(GFP_KERNEL); + if (!call) { + _leave(" ENOMEM"); + return -ENOMEM; + } + + atomic_set(&call->usage,1); + + init_waitqueue_head(&call->waitq); + spin_lock_init(&call->lock); + INIT_LIST_HEAD(&call->link); + INIT_LIST_HEAD(&call->acks_pendq); + INIT_LIST_HEAD(&call->rcv_receiveq); + INIT_LIST_HEAD(&call->rcv_krxiodq_lk); + INIT_LIST_HEAD(&call->app_readyq); + INIT_LIST_HEAD(&call->app_unreadyq); + INIT_LIST_HEAD(&call->app_link); + INIT_LIST_HEAD(&call->app_attn_link); + + init_timer(&call->acks_timeout); + call->acks_timeout.data = (unsigned long) call; + call->acks_timeout.function = __rxrpc_call_acks_timeout; + + init_timer(&call->rcv_timeout); + call->rcv_timeout.data = (unsigned long) call; + call->rcv_timeout.function = __rxrpc_call_rcv_timeout; + + init_timer(&call->ackr_dfr_timo); + call->ackr_dfr_timo.data = (unsigned long) call; + call->ackr_dfr_timo.function = __rxrpc_call_ackr_timeout; + + call->conn = conn; + call->ackr_win_bot = 1; + call->ackr_win_top = call->ackr_win_bot + RXRPC_CALL_ACK_WINDOW_SIZE - 1; + call->ackr_prev_seq = 0; + call->app_mark = RXRPC_APP_MARK_EOF; + call->app_attn_func = rxrpc_call_default_attn_func; + call->app_error_func = rxrpc_call_default_error_func; + call->app_aemap_func = rxrpc_call_default_aemap_func; + call->app_scr_alloc = call->app_scratch; + + call->cjif = jiffies; + + _leave(" = 0 (%p)",call); + + *_call = call; + + return 0; +} /* end __rxrpc_create_call() */ + +/*****************************************************************************/ +/* + * create a new call record for outgoing calls + */ +int rxrpc_create_call(struct rxrpc_connection *conn, + rxrpc_call_attn_func_t attn, + rxrpc_call_error_func_t error, + rxrpc_call_aemap_func_t aemap, + struct rxrpc_call **_call) +{ + DECLARE_WAITQUEUE(myself,current); + + struct rxrpc_call *call; + int ret, cix, loop; + + _enter("%p",conn); + + /* allocate and initialise a call record */ + ret = __rxrpc_create_call(conn,&call); + if (ret<0) { + _leave(" = %d",ret); + return ret; + } + + call->app_call_state = RXRPC_CSTATE_CLNT_SND_ARGS; + if (attn) call->app_attn_func = attn; + if (error) call->app_error_func = error; + if (aemap) call->app_aemap_func = aemap; + + _state(call); + + spin_lock(&conn->lock); + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&conn->chanwait,&myself); + + try_again: + /* try to find an unused channel */ + for (cix=0; cix<4; cix++) + if (!conn->channels[cix]) + goto obtained_chan; + + /* no free channels - wait for one to become available */ + ret = -EINTR; + if (signal_pending(current)) + goto error_unwait; + + spin_unlock(&conn->lock); + + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock(&conn->lock); + goto try_again; + + /* got a channel - now attach to the connection */ + obtained_chan: + remove_wait_queue(&conn->chanwait,&myself); + set_current_state(TASK_RUNNING); + + /* concoct a unique call number */ + next_callid: + call->call_id = htonl(++conn->call_counter); + for (loop=0; loop<4; loop++) + if (conn->channels[loop] && conn->channels[loop]->call_id==call->call_id) + goto next_callid; + + rxrpc_get_connection(conn); + conn->channels[cix] = call; /* assign _after_ done callid check loop */ + do_gettimeofday(&conn->atime); + call->chan_ix = htonl(cix); + + spin_unlock(&conn->lock); + + down_write(&rxrpc_calls_sem); + list_add_tail(&call->call_link,&rxrpc_calls); + up_write(&rxrpc_calls_sem); + + __RXACCT(atomic_inc(&rxrpc_call_count)); + *_call = call; + + _leave(" = 0 (call=%p cix=%u)",call,cix); + return 0; + + error_unwait: + remove_wait_queue(&conn->chanwait,&myself); + set_current_state(TASK_RUNNING); + spin_unlock(&conn->lock); + + free_page((unsigned long)call); + _leave(" = %d",ret); + return ret; + +} /* end rxrpc_create_call() */ + +/*****************************************************************************/ +/* + * create a new call record for incoming calls + */ +int rxrpc_incoming_call(struct rxrpc_connection *conn, + struct rxrpc_message *msg, + struct rxrpc_call **_call) +{ + struct rxrpc_call *call; + unsigned cix; + int ret; + + cix = ntohl(msg->hdr.cid) & RXRPC_CHANNELMASK; + + _enter("%p,%u,%u",conn,ntohl(msg->hdr.callNumber),cix); + + /* allocate and initialise a call record */ + ret = __rxrpc_create_call(conn,&call); + if (ret<0) { + _leave(" = %d",ret); + return ret; + } + + call->pkt_rcv_count = 1; + call->app_call_state = RXRPC_CSTATE_SRVR_RCV_OPID; + call->app_mark = sizeof(u32); + + _state(call); + + /* attach to the connection */ + ret = -EBUSY; + call->chan_ix = htonl(cix); + call->call_id = msg->hdr.callNumber; + + spin_lock(&conn->lock); + + if (!conn->channels[cix]) { + conn->channels[cix] = call; + rxrpc_get_connection(conn); + ret = 0; + } + + spin_unlock(&conn->lock); + + if (ret<0) free_page((unsigned long)call); + + _leave(" = %p",call); + + if (ret==0) { + down_write(&rxrpc_calls_sem); + list_add_tail(&call->call_link,&rxrpc_calls); + up_write(&rxrpc_calls_sem); + __RXACCT(atomic_inc(&rxrpc_call_count)); + *_call = call; + } + + return ret; +} /* end rxrpc_incoming_call() */ + +/*****************************************************************************/ +/* + * free a call record + */ +void rxrpc_put_call(struct rxrpc_call *call) +{ + struct rxrpc_connection *conn = call->conn; + struct rxrpc_message *msg; + + _enter("%p{u=%d}",call,atomic_read(&call->usage)); + + /* sanity check */ + if (atomic_read(&call->usage)<=0) + BUG(); + + /* to prevent a race, the decrement and the de-list must be effectively atomic */ + spin_lock(&conn->lock); + if (likely(!atomic_dec_and_test(&call->usage))) { + spin_unlock(&conn->lock); + _leave(""); + return; + } + + conn->channels[ntohl(call->chan_ix)] = NULL; + + spin_unlock(&conn->lock); + + wake_up(&conn->chanwait); + + rxrpc_put_connection(conn); + + /* clear the timers and dequeue from krxiod */ + del_timer_sync(&call->acks_timeout); + del_timer_sync(&call->rcv_timeout); + del_timer_sync(&call->ackr_dfr_timo); + + rxrpc_krxiod_dequeue_call(call); + + /* clean up the contents of the struct */ + if (call->snd_nextmsg) + rxrpc_put_message(call->snd_nextmsg); + + if (call->snd_ping) + rxrpc_put_message(call->snd_ping); + + while (!list_empty(&call->acks_pendq)) { + msg = list_entry(call->acks_pendq.next,struct rxrpc_message,link); + list_del(&msg->link); + rxrpc_put_message(msg); + } + + while (!list_empty(&call->rcv_receiveq)) { + msg = list_entry(call->rcv_receiveq.next,struct rxrpc_message,link); + list_del(&msg->link); + rxrpc_put_message(msg); + } + + while (!list_empty(&call->app_readyq)) { + msg = list_entry(call->app_readyq.next,struct rxrpc_message,link); + list_del(&msg->link); + rxrpc_put_message(msg); + } + + while (!list_empty(&call->app_unreadyq)) { + msg = list_entry(call->app_unreadyq.next,struct rxrpc_message,link); + list_del(&msg->link); + rxrpc_put_message(msg); + } + + if (call->owner) __MOD_DEC_USE_COUNT(call->owner); + + down_write(&rxrpc_calls_sem); + list_del(&call->call_link); + up_write(&rxrpc_calls_sem); + + __RXACCT(atomic_dec(&rxrpc_call_count)); + free_page((unsigned long)call); + + _leave(" [destroyed]"); +} /* end rxrpc_put_call() */ + +/*****************************************************************************/ +/* + * actually generate a normal ACK + */ +static inline int __rxrpc_call_gen_normal_ACK(struct rxrpc_call *call, rxrpc_seq_t seq) +{ + struct rxrpc_message *msg; + struct iovec diov[3]; + unsigned aux[4]; + int delta, ret; + + /* ACKs default to DELAY */ + if (!call->ackr.reason) + call->ackr.reason = RXRPC_ACK_DELAY; + + _proto("Rx %05lu Sending ACK { m=%hu f=#%u p=#%u s=%%%u r=%s n=%u }", + jiffies - call->cjif, + ntohs(call->ackr.maxSkew), + ntohl(call->ackr.firstPacket), + ntohl(call->ackr.previousPacket), + ntohl(call->ackr.serial), + rxrpc_acks[call->ackr.reason], + call->ackr.nAcks); + + aux[0] = htonl(call->conn->peer->if_mtu); /* interface MTU */ + aux[1] = htonl(1444); /* max MTU */ + aux[2] = htonl(16); /* rwind */ + aux[3] = htonl(4); /* max packets */ + + diov[0].iov_len = sizeof(struct rxrpc_ackpacket); + diov[0].iov_base = &call->ackr; + diov[1].iov_len = (call->ackr_pend_cnt+3); + diov[1].iov_base = call->ackr_array; + diov[2].iov_len = sizeof(aux); + diov[2].iov_base = &aux; + + /* build and send the message */ + ret = rxrpc_conn_newmsg(call->conn,call,RXRPC_PACKET_TYPE_ACK,3,diov,GFP_KERNEL,&msg); + if (ret<0) + goto out; + + msg->seq = seq; + msg->hdr.seq = htonl(seq); + msg->hdr.flags |= RXRPC_SLOW_START_OK; + + ret = rxrpc_conn_sendmsg(call->conn,msg); + rxrpc_put_message(msg); + if (ret<0) + goto out; + call->pkt_snd_count++; + + /* count how many actual ACKs there were at the front */ + for (delta=0; deltaackr_pend_cnt; delta++) + if (call->ackr_array[delta]!=RXRPC_ACK_TYPE_ACK) + break; + + call->ackr_pend_cnt -= delta; /* all ACK'd to this point */ + + /* crank the ACK window around */ + if (delta==0) { + /* un-ACK'd window */ + } + else if (delta < RXRPC_CALL_ACK_WINDOW_SIZE) { + /* partially ACK'd window + * - shuffle down to avoid losing out-of-sequence packets + */ + call->ackr_win_bot += delta; + call->ackr_win_top += delta; + + memmove(&call->ackr_array[0], + &call->ackr_array[delta], + call->ackr_pend_cnt); + + memset(&call->ackr_array[call->ackr_pend_cnt], + RXRPC_ACK_TYPE_NACK, + sizeof(call->ackr_array) - call->ackr_pend_cnt); + } + else { + /* fully ACK'd window + * - just clear the whole thing + */ + memset(&call->ackr_array,RXRPC_ACK_TYPE_NACK,sizeof(call->ackr_array)); + } + + /* clear this ACK */ + memset(&call->ackr,0,sizeof(call->ackr)); + + out: + if (!call->app_call_state) printk("___ STATE 0 ___\n"); + return ret; +} /* end __rxrpc_call_gen_normal_ACK() */ + +/*****************************************************************************/ +/* + * note the reception of a packet in the call's ACK records and generate an appropriate ACK packet + * if necessary + * - returns 0 if packet should be processed, 1 if packet should be ignored and -ve on an error + */ +static int rxrpc_call_generate_ACK(struct rxrpc_call *call, + struct rxrpc_header *hdr, + struct rxrpc_ackpacket *ack) +{ + struct rxrpc_message *msg; + rxrpc_seq_t seq; + unsigned offset; + int ret = 0, err; + u8 special_ACK, do_ACK, force; + + _enter("%p,%p { seq=%d tp=%d fl=%02x }",call,hdr,ntohl(hdr->seq),hdr->type,hdr->flags); + + seq = ntohl(hdr->seq); + offset = seq - call->ackr_win_bot; + do_ACK = RXRPC_ACK_DELAY; + special_ACK = 0; + force = (seq==1); + + if (call->ackr_high_seq < seq) + call->ackr_high_seq = seq; + + /* deal with generation of obvious special ACKs first */ + if (ack && ack->reason==RXRPC_ACK_PING) { + special_ACK = RXRPC_ACK_PING_RESPONSE; + ret = 1; + goto gen_ACK; + } + + if (seq < call->ackr_win_bot) { + special_ACK = RXRPC_ACK_DUPLICATE; + ret = 1; + goto gen_ACK; + } + + if (seq >= call->ackr_win_top) { + special_ACK = RXRPC_ACK_EXCEEDS_WINDOW; + ret = 1; + goto gen_ACK; + } + + if (call->ackr_array[offset] != RXRPC_ACK_TYPE_NACK) { + special_ACK = RXRPC_ACK_DUPLICATE; + ret = 1; + goto gen_ACK; + } + + /* okay... it's a normal data packet inside the ACK window */ + call->ackr_array[offset] = RXRPC_ACK_TYPE_ACK; + + if (offsetackr_pend_cnt) { + } + else if (offset>call->ackr_pend_cnt) { + do_ACK = RXRPC_ACK_OUT_OF_SEQUENCE; + call->ackr_pend_cnt = offset; + goto gen_ACK; + } + + if (hdr->flags & RXRPC_REQUEST_ACK) { + do_ACK = RXRPC_ACK_REQUESTED; + } + + /* generate an ACK on the final packet of a reply just received */ + if (hdr->flags & RXRPC_LAST_PACKET) { + if (call->conn->out_clientflag) + force = 1; + } + else if (!(hdr->flags & RXRPC_MORE_PACKETS)) { + do_ACK = RXRPC_ACK_REQUESTED; + } + + /* re-ACK packets previously received out-of-order */ + for (offset++; offsetackr_array[offset]!=RXRPC_ACK_TYPE_ACK) + break; + + call->ackr_pend_cnt = offset; + + /* generate an ACK if we fill up the window */ + if (call->ackr_pend_cnt >= RXRPC_CALL_ACK_WINDOW_SIZE) + force = 1; + + gen_ACK: + _debug("%05lu ACKs pend=%u norm=%s special=%s%s", + jiffies - call->cjif, + call->ackr_pend_cnt,rxrpc_acks[do_ACK],rxrpc_acks[special_ACK], + force ? " immediate" : + do_ACK==RXRPC_ACK_REQUESTED ? " merge-req" : + hdr->flags & RXRPC_LAST_PACKET ? " finalise" : + " defer" + ); + + /* send any pending normal ACKs if need be */ + if (call->ackr_pend_cnt>0) { + /* fill out the appropriate form */ + call->ackr.bufferSpace = htons(RXRPC_CALL_ACK_WINDOW_SIZE); + call->ackr.maxSkew = htons(min(call->ackr_high_seq - seq,65535U)); + call->ackr.firstPacket = htonl(call->ackr_win_bot); + call->ackr.previousPacket = call->ackr_prev_seq; + call->ackr.serial = hdr->serial; + call->ackr.nAcks = call->ackr_pend_cnt; + + if (do_ACK==RXRPC_ACK_REQUESTED) + call->ackr.reason = do_ACK; + + /* generate the ACK immediately if necessary */ + if (special_ACK || force) { + err = __rxrpc_call_gen_normal_ACK(call,do_ACK==RXRPC_ACK_DELAY ? 0 : seq); + if (err<0) { + ret = err; + goto out; + } + } + } + + if (call->ackr.reason==RXRPC_ACK_REQUESTED) + call->ackr_dfr_seq = seq; + + /* start the ACK timer if not running if there are any pending deferred ACKs */ + if (call->ackr_pend_cnt>0 && + call->ackr.reason!=RXRPC_ACK_REQUESTED && + !timer_pending(&call->ackr_dfr_timo) + ) { + unsigned long timo; + + timo = rxrpc_call_dfr_ack_timeout + jiffies; + + _debug("START ACKR TIMER for cj=%lu",timo-call->cjif); + + spin_lock(&call->lock); + mod_timer(&call->ackr_dfr_timo,timo); + spin_unlock(&call->lock); + } + else if ((call->ackr_pend_cnt==0 || call->ackr.reason==RXRPC_ACK_REQUESTED) && + timer_pending(&call->ackr_dfr_timo) + ) { + /* stop timer if no pending ACKs */ + _debug("CLEAR ACKR TIMER"); + del_timer_sync(&call->ackr_dfr_timo); + } + + /* send a special ACK if one is required */ + if (special_ACK) { + struct rxrpc_ackpacket ack; + struct iovec diov[2]; + u8 acks[1] = { RXRPC_ACK_TYPE_ACK }; + + /* fill out the appropriate form */ + ack.bufferSpace = htons(RXRPC_CALL_ACK_WINDOW_SIZE); + ack.maxSkew = htons(min(call->ackr_high_seq - seq,65535U)); + ack.firstPacket = htonl(call->ackr_win_bot); + ack.previousPacket = call->ackr_prev_seq; + ack.serial = hdr->serial; + ack.reason = special_ACK; + ack.nAcks = 0; + //ack.nAcks = special_ACK==RXRPC_ACK_OUT_OF_SEQUENCE ? 0 : hdr->seq ? 1 : 0; + + _proto("Rx Sending s-ACK { m=%hu f=#%u p=#%u s=%%%u r=%s n=%u }", + ntohs(ack.maxSkew),ntohl(ack.firstPacket),ntohl(ack.previousPacket), + ntohl(ack.serial),rxrpc_acks[ack.reason],ack.nAcks); + + diov[0].iov_len = sizeof(struct rxrpc_ackpacket); + diov[0].iov_base = &ack; + diov[1].iov_len = sizeof(acks); + diov[1].iov_base = acks; + + /* build and send the message */ + err = rxrpc_conn_newmsg(call->conn,call,RXRPC_PACKET_TYPE_ACK, + hdr->seq ? 2 : 1,diov, + GFP_KERNEL, + &msg); + if (err<0) { + ret = err; + goto out; + } + + msg->seq = seq; + msg->hdr.seq = htonl(seq); + msg->hdr.flags |= RXRPC_SLOW_START_OK; + + err = rxrpc_conn_sendmsg(call->conn,msg); + rxrpc_put_message(msg); + if (err<0) { + ret = err; + goto out; + } + call->pkt_snd_count++; + } + + out: + if (hdr->seq) + call->ackr_prev_seq = hdr->seq; + + _leave(" = %d",ret); + return ret; +} /* end rxrpc_call_generate_ACK() */ + +/*****************************************************************************/ +/* + * handle work to be done on a call + * - includes packet reception and timeout processing + */ +void rxrpc_call_do_stuff(struct rxrpc_call *call) +{ + _enter("%p{flags=%lx}",call,call->flags); + + /* handle packet reception */ + if (call->flags & RXRPC_CALL_RCV_PKT) { + _debug("- receive packet"); + call->flags &= ~RXRPC_CALL_RCV_PKT; + rxrpc_call_receive_packet(call); + } + + /* handle overdue ACKs */ + if (call->flags & RXRPC_CALL_ACKS_TIMO) { + _debug("- overdue ACK timeout"); + call->flags &= ~RXRPC_CALL_ACKS_TIMO; + rxrpc_call_resend(call,call->snd_seq_count); + } + + /* handle lack of reception */ + if (call->flags & RXRPC_CALL_RCV_TIMO) { + _debug("- reception timeout"); + call->flags &= ~RXRPC_CALL_RCV_TIMO; + rxrpc_call_abort(call,-EIO); + } + + /* handle deferred ACKs */ + if (call->flags & RXRPC_CALL_ACKR_TIMO || + (call->ackr.nAcks>0 && call->ackr.reason==RXRPC_ACK_REQUESTED) + ) { + _debug("- deferred ACK timeout: cj=%05lu r=%s n=%u", + jiffies - call->cjif, + rxrpc_acks[call->ackr.reason], + call->ackr.nAcks); + + call->flags &= ~RXRPC_CALL_ACKR_TIMO; + + if (call->ackr.nAcks>0 && call->app_call_state!=RXRPC_CSTATE_ERROR) { + /* generate ACK */ + __rxrpc_call_gen_normal_ACK(call,call->ackr_dfr_seq); + call->ackr_dfr_seq = 0; + } + } + + _leave(""); + +} /* end rxrpc_call_do_timeout() */ + +/*****************************************************************************/ +/* + * send an abort message at call or connection level + * - must be called with call->lock held + * - the supplied error code is sent as the packet data + */ +static int __rxrpc_call_abort(struct rxrpc_call *call, int errno) +{ + struct rxrpc_connection *conn = call->conn; + struct rxrpc_message *msg; + struct iovec diov[1]; + int ret; + u32 _error; + + _enter("%p{%08x},%p{%d},%d",conn,ntohl(conn->conn_id),call,ntohl(call->call_id),errno); + + /* if this call is already aborted, then just wake up any waiters */ + if (call->app_call_state==RXRPC_CSTATE_ERROR) { + spin_unlock(&call->lock); + call->app_error_func(call); + _leave(" = 0"); + return 0; + } + + rxrpc_get_call(call); + + /* change the state _with_ the lock still held */ + call->app_call_state = RXRPC_CSTATE_ERROR; + call->app_err_state = RXRPC_ESTATE_LOCAL_ABORT; + call->app_errno = errno; + call->app_mark = RXRPC_APP_MARK_EOF; + call->app_read_buf = NULL; + call->app_async_read = 0; + + _state(call); + + /* ask the app to translate the error code */ + call->app_aemap_func(call); + + spin_unlock(&call->lock); + + /* flush any outstanding ACKs */ + del_timer_sync(&call->acks_timeout); + del_timer_sync(&call->rcv_timeout); + del_timer_sync(&call->ackr_dfr_timo); + + if (rxrpc_call_is_ack_pending(call)) + __rxrpc_call_gen_normal_ACK(call,0); + + /* send the abort packet only if we actually traded some other packets */ + ret = 0; + if (call->pkt_snd_count || call->pkt_rcv_count) { + /* actually send the abort */ + _proto("Rx Sending Call ABORT { data=%d }",call->app_abort_code); + + _error = htonl(call->app_abort_code); + + diov[0].iov_len = sizeof(_error); + diov[0].iov_base = &_error; + + ret = rxrpc_conn_newmsg(conn,call,RXRPC_PACKET_TYPE_ABORT,1,diov,GFP_KERNEL,&msg); + if (ret==0) { + ret = rxrpc_conn_sendmsg(conn,msg); + rxrpc_put_message(msg); + } + } + + /* tell the app layer to let go */ + call->app_error_func(call); + + rxrpc_put_call(call); + + _leave(" = %d",ret); + + return ret; +} /* end __rxrpc_call_abort() */ + +/*****************************************************************************/ +/* + * send an abort message at call or connection level + * - the supplied error code is sent as the packet data + */ +int rxrpc_call_abort(struct rxrpc_call *call, int error) +{ + spin_lock(&call->lock); + + return __rxrpc_call_abort(call,error); + +} /* end rxrpc_call_abort() */ + +/*****************************************************************************/ +/* + * process packets waiting for this call + */ +static void rxrpc_call_receive_packet(struct rxrpc_call *call) +{ + struct rxrpc_message *msg; + struct list_head *_p; + u32 data32; + + _enter("%p",call); + + rxrpc_get_call(call); /* must not go away too soon if aborted by app-layer */ + + while (!list_empty(&call->rcv_receiveq)) { + /* try to get next packet */ + _p = NULL; + spin_lock(&call->lock); + if (!list_empty(&call->rcv_receiveq)) { + _p = call->rcv_receiveq.next; + list_del_init(_p); + } + spin_unlock(&call->lock); + + if (!_p) break; + + msg = list_entry(_p,struct rxrpc_message,link); + + _proto("Rx %05lu Received %s packet (%%%u,#%u,%c%c%c%c%c)", + jiffies - call->cjif, + rxrpc_pkts[msg->hdr.type], + ntohl(msg->hdr.serial), + msg->seq, + msg->hdr.flags & RXRPC_JUMBO_PACKET ? 'j' : '-', + msg->hdr.flags & RXRPC_MORE_PACKETS ? 'm' : '-', + msg->hdr.flags & RXRPC_LAST_PACKET ? 'l' : '-', + msg->hdr.flags & RXRPC_REQUEST_ACK ? 'r' : '-', + msg->hdr.flags & RXRPC_CLIENT_INITIATED ? 'C' : 'S' + ); + + switch (msg->hdr.type) { + /* deal with data packets */ + case RXRPC_PACKET_TYPE_DATA: + /* ACK the packet if necessary */ + switch (rxrpc_call_generate_ACK(call,&msg->hdr,NULL)) { + case 0: /* useful packet */ + rxrpc_call_receive_data_packet(call,msg); + break; + case 1: /* duplicate or out-of-window packet */ + break; + default: + rxrpc_put_message(msg); + goto out; + } + break; + + /* deal with ACK packets */ + case RXRPC_PACKET_TYPE_ACK: + rxrpc_call_receive_ack_packet(call,msg); + break; + + /* deal with abort packets */ + case RXRPC_PACKET_TYPE_ABORT: + data32 = 0; + if (skb_copy_bits(msg->pkt,msg->offset,&data32,sizeof(data32))<0) { + printk("Rx Received short ABORT packet\n"); + } + else { + data32 = ntohl(data32); + } + + _proto("Rx Received Call ABORT { data=%d }",data32); + + spin_lock(&call->lock); + call->app_call_state = RXRPC_CSTATE_ERROR; + call->app_err_state = RXRPC_ESTATE_PEER_ABORT; + call->app_abort_code = data32; + call->app_errno = -ECONNABORTED; + call->app_mark = RXRPC_APP_MARK_EOF; + call->app_read_buf = NULL; + call->app_async_read = 0; + + /* ask the app to translate the error code */ + call->app_aemap_func(call); + _state(call); + spin_unlock(&call->lock); + call->app_error_func(call); + break; + + default: + /* deal with other packet types */ + _proto("Rx Unsupported packet type %u (#%u)",msg->hdr.type,msg->seq); + break; + } + + rxrpc_put_message(msg); + } + + out: + rxrpc_put_call(call); + _leave(""); +} /* end rxrpc_call_receive_packet() */ + +/*****************************************************************************/ +/* + * process next data packet + * - as the next data packet arrives: + * - it is queued on app_readyq _if_ it is the next one expected (app_ready_seq+1) + * - it is queued on app_unreadyq _if_ it is not the next one expected + * - if a packet placed on app_readyq completely fills a hole leading up to the first packet + * on app_unreadyq, then packets now in sequence are tranferred to app_readyq + * - the application layer can only see packets on app_readyq (app_ready_qty bytes) + * - the application layer is prodded every time a new packet arrives + */ +static void rxrpc_call_receive_data_packet(struct rxrpc_call *call, struct rxrpc_message *msg) +{ + const struct rxrpc_operation *optbl, *op; + struct rxrpc_message *pmsg; + struct list_head *_p; + int ret, lo, hi, rmtimo; + u32 opid; + + _enter("%p{%u},%p{%u}",call,ntohl(call->call_id),msg,msg->seq); + + rxrpc_get_message(msg); + + /* add to the unready queue if we'd have to create a hole in the ready queue otherwise */ + if (msg->seq != call->app_ready_seq+1) { + _debug("Call add packet %d to unreadyq",msg->seq); + + /* insert in seq order */ + list_for_each(_p,&call->app_unreadyq) { + pmsg = list_entry(_p,struct rxrpc_message,link); + if (pmsg->seq>msg->seq) + break; + } + + list_add_tail(&msg->link,_p); + + _leave(" [unreadyq]"); + return; + } + + /* next in sequence - simply append into the call's ready queue */ + _debug("Call add packet %d to readyq (+%d => %d bytes)", + msg->seq,msg->dsize,call->app_ready_qty); + + spin_lock(&call->lock); + call->app_ready_seq = msg->seq; + call->app_ready_qty += msg->dsize; + list_add_tail(&msg->link,&call->app_readyq); + + /* move unready packets to the readyq if we got rid of a hole */ + while (!list_empty(&call->app_unreadyq)) { + pmsg = list_entry(call->app_unreadyq.next,struct rxrpc_message,link); + + if (pmsg->seq != call->app_ready_seq+1) + break; + + /* next in sequence - just move list-to-list */ + _debug("Call transfer packet %d to readyq (+%d => %d bytes)", + pmsg->seq,pmsg->dsize,call->app_ready_qty); + + call->app_ready_seq = pmsg->seq; + call->app_ready_qty += pmsg->dsize; + list_del_init(&pmsg->link); + list_add_tail(&pmsg->link,&call->app_readyq); + } + + /* see if we've got the last packet yet */ + if (!list_empty(&call->app_readyq)) { + pmsg = list_entry(call->app_readyq.prev,struct rxrpc_message,link); + if (pmsg->hdr.flags & RXRPC_LAST_PACKET) { + call->app_last_rcv = 1; + _debug("Last packet on readyq"); + } + } + + switch (call->app_call_state) { + /* do nothing if call already aborted */ + case RXRPC_CSTATE_ERROR: + spin_unlock(&call->lock); + _leave(" [error]"); + return; + + /* extract the operation ID from an incoming call if that's not yet been done */ + case RXRPC_CSTATE_SRVR_RCV_OPID: + spin_unlock(&call->lock); + + /* handle as yet insufficient data for the operation ID */ + if (call->app_ready_qty<4) { + if (call->app_last_rcv) + rxrpc_call_abort(call,-EINVAL); /* trouble - last packet seen */ + + _leave(""); + return; + } + + /* pull the operation ID out of the buffer */ + ret = rxrpc_call_read_data(call,&opid,sizeof(opid),0); + if (ret<0) { + printk("Unexpected error from read-data: %d\n",ret); + if (call->app_call_state!=RXRPC_CSTATE_ERROR) + rxrpc_call_abort(call,ret); + _leave(""); + return; + } + call->app_opcode = ntohl(opid); + + /* locate the operation in the available ops table */ + optbl = call->conn->service->ops_begin; + lo = 0; + hi = call->conn->service->ops_end - optbl; + + while (loapp_opcode==op->id) + goto found_op; + if (call->app_opcode>op->id) + lo = mid+1; + else + hi = mid; + } + + /* search failed */ + kproto("Rx Client requested operation %d from %s service", + call->app_opcode,call->conn->service->name); + rxrpc_call_abort(call,-EINVAL); + _leave(" [inval]"); + return; + + found_op: + _proto("Rx Client requested operation %s from %s service", + op->name,call->conn->service->name); + + /* we're now waiting for the argument block (unless the call was aborted) */ + spin_lock(&call->lock); + if (call->app_call_state==RXRPC_CSTATE_SRVR_RCV_OPID || + call->app_call_state==RXRPC_CSTATE_SRVR_SND_REPLY) { + if (!call->app_last_rcv) + call->app_call_state = RXRPC_CSTATE_SRVR_RCV_ARGS; + else if (call->app_ready_qty>0) + call->app_call_state = RXRPC_CSTATE_SRVR_GOT_ARGS; + else + call->app_call_state = RXRPC_CSTATE_SRVR_SND_REPLY; + call->app_mark = op->asize; + call->app_user = op->user; + } + spin_unlock(&call->lock); + + _state(call); + break; + + case RXRPC_CSTATE_SRVR_RCV_ARGS: + /* change state if just received last packet of arg block */ + if (call->app_last_rcv) + call->app_call_state = RXRPC_CSTATE_SRVR_GOT_ARGS; + spin_unlock(&call->lock); + + _state(call); + break; + + case RXRPC_CSTATE_CLNT_RCV_REPLY: + /* change state if just received last packet of reply block */ + rmtimo = 0; + if (call->app_last_rcv) { + call->app_call_state = RXRPC_CSTATE_CLNT_GOT_REPLY; + rmtimo = 1; + } + spin_unlock(&call->lock); + + if (rmtimo) { + del_timer_sync(&call->acks_timeout); + del_timer_sync(&call->rcv_timeout); + del_timer_sync(&call->ackr_dfr_timo); + } + + _state(call); + break; + + default: + /* deal with data reception in an unexpected state */ + printk("Unexpected state [[[ %u ]]]\n",call->app_call_state); + __rxrpc_call_abort(call,-EBADMSG); + _leave(""); + return; + } + + if (call->app_call_state==RXRPC_CSTATE_CLNT_RCV_REPLY && call->app_last_rcv) + BUG(); + + /* otherwise just invoke the data function whenever we can satisfy its desire for more + * data + */ + _proto("Rx Received Op Data: st=%u qty=%u mk=%u%s", + call->app_call_state,call->app_ready_qty,call->app_mark, + call->app_last_rcv ? " last-rcvd" : ""); + + spin_lock(&call->lock); + + ret = __rxrpc_call_read_data(call); + switch (ret) { + case 0: + spin_unlock(&call->lock); + call->app_attn_func(call); + break; + case -EAGAIN: + spin_unlock(&call->lock); + break; + case -ECONNABORTED: + spin_unlock(&call->lock); + break; + default: + __rxrpc_call_abort(call,ret); + break; + } + + _state(call); + + _leave(""); + +} /* end rxrpc_call_receive_data_packet() */ + +/*****************************************************************************/ +/* + * received an ACK packet + */ +static void rxrpc_call_receive_ack_packet(struct rxrpc_call *call, struct rxrpc_message *msg) +{ + struct rxrpc_ackpacket ack; + rxrpc_serial_t serial; + rxrpc_seq_t seq; + int ret; + + _enter("%p{%u},%p{%u}",call,ntohl(call->call_id),msg,msg->seq); + + /* extract the basic ACK record */ + if (skb_copy_bits(msg->pkt,msg->offset,&ack,sizeof(ack))<0) { + printk("Rx Received short ACK packet\n"); + return; + } + msg->offset += sizeof(ack); + + serial = ack.serial; + seq = ntohl(ack.firstPacket); + + _proto("Rx Received ACK %%%d { b=%hu m=%hu f=%u p=%u s=%u r=%s n=%u }", + ntohl(msg->hdr.serial), + ntohs(ack.bufferSpace), + ntohs(ack.maxSkew), + seq, + ntohl(ack.previousPacket), + ntohl(serial), + rxrpc_acks[ack.reason], + call->ackr.nAcks + ); + + /* check the other side isn't ACK'ing a sequence number I haven't sent yet */ + if (ack.nAcks>0 && (seq > call->snd_seq_count || seq+ack.nAcks-1 > call->snd_seq_count)) { + printk("Received ACK (#%u-#%u) for unsent packet\n",seq,seq+ack.nAcks-1); + rxrpc_call_abort(call,-EINVAL); + _leave(""); + return; + } + + /* deal with RTT calculation */ + if (serial) { + struct rxrpc_message *rttmsg; + + /* find the prompting packet */ + spin_lock(&call->lock); + if (call->snd_ping && call->snd_ping->hdr.serial==serial) { + /* it was a ping packet */ + rttmsg = call->snd_ping; + call->snd_ping = NULL; + spin_unlock(&call->lock); + + if (rttmsg) { + rttmsg->rttdone = 1; + rxrpc_peer_calculate_rtt(call->conn->peer,rttmsg,msg); + rxrpc_put_message(rttmsg); + } + } + else { + struct list_head *_p; + + /* it ought to be a data packet - look in the pending ACK list */ + list_for_each(_p,&call->acks_pendq) { + rttmsg = list_entry(_p,struct rxrpc_message,link); + if (rttmsg->hdr.serial==serial) { + if (rttmsg->rttdone) + break; /* never do RTT twice without resending */ + + rttmsg->rttdone = 1; + rxrpc_peer_calculate_rtt(call->conn->peer,rttmsg,msg); + break; + } + } + spin_unlock(&call->lock); + } + } + + switch (ack.reason) { + /* deal with negative/positive acknowledgement of data packets */ + case RXRPC_ACK_REQUESTED: + case RXRPC_ACK_DELAY: + case RXRPC_ACK_IDLE: + rxrpc_call_definitively_ACK(call,seq-1); + + case RXRPC_ACK_DUPLICATE: + case RXRPC_ACK_OUT_OF_SEQUENCE: + case RXRPC_ACK_EXCEEDS_WINDOW: + call->snd_resend_cnt = 0; + ret = rxrpc_call_record_ACK(call,msg,seq,ack.nAcks); + if (ret<0) + rxrpc_call_abort(call,ret); + break; + + /* respond to ping packets immediately */ + case RXRPC_ACK_PING: + rxrpc_call_generate_ACK(call,&msg->hdr,&ack); + break; + + /* only record RTT on ping response packets */ + case RXRPC_ACK_PING_RESPONSE: + if (call->snd_ping) { + struct rxrpc_message *rttmsg; + + /* only do RTT stuff if the response matches the retained ping */ + rttmsg = NULL; + spin_lock(&call->lock); + if (call->snd_ping && call->snd_ping->hdr.serial==ack.serial) { + rttmsg = call->snd_ping; + call->snd_ping = NULL; + } + spin_unlock(&call->lock); + + if (rttmsg) { + rttmsg->rttdone = 1; + rxrpc_peer_calculate_rtt(call->conn->peer,rttmsg,msg); + rxrpc_put_message(rttmsg); + } + } + break; + + default: + printk("Unsupported ACK reason %u\n",ack.reason); + break; + } + + _leave(""); +} /* end rxrpc_call_receive_ack_packet() */ + +/*****************************************************************************/ +/* + * record definitive ACKs for all messages up to and including the one with the 'highest' seq + */ +static void rxrpc_call_definitively_ACK(struct rxrpc_call *call, rxrpc_seq_t highest) +{ + struct rxrpc_message *msg; + int now_complete; + + _enter("%p{ads=%u},%u",call,call->acks_dftv_seq,highest); + + while (call->acks_dftv_seqacks_dftv_seq++; + + _proto("Definitive ACK on packet #%u",call->acks_dftv_seq); + + /* discard those at front of queue until message with highest ACK is found */ + spin_lock(&call->lock); + msg = NULL; + if (!list_empty(&call->acks_pendq)) { + msg = list_entry(call->acks_pendq.next,struct rxrpc_message,link); + list_del_init(&msg->link); /* dequeue */ + if (msg->state==RXRPC_MSG_SENT) + call->acks_pend_cnt--; + } + spin_unlock(&call->lock); + + /* insanity check */ + if (!msg) + panic("%s(): acks_pendq unexpectedly empty\n",__FUNCTION__); + + if (msg->seq!=call->acks_dftv_seq) + panic("%s(): Packet #%u expected at front of acks_pendq (#%u found)\n", + __FUNCTION__,call->acks_dftv_seq,msg->seq); + + /* discard the message */ + msg->state = RXRPC_MSG_DONE; + rxrpc_put_message(msg); + } + + /* if all sent packets are definitively ACK'd then prod any sleepers just in case */ + now_complete = 0; + spin_lock(&call->lock); + if (call->acks_dftv_seq==call->snd_seq_count) { + if (call->app_call_state!=RXRPC_CSTATE_COMPLETE) { + call->app_call_state = RXRPC_CSTATE_COMPLETE; + _state(call); + now_complete = 1; + } + } + spin_unlock(&call->lock); + + if (now_complete) { + del_timer_sync(&call->acks_timeout); + del_timer_sync(&call->rcv_timeout); + del_timer_sync(&call->ackr_dfr_timo); + call->app_attn_func(call); + } + + _leave(""); +} /* end rxrpc_call_definitively_ACK() */ + +/*****************************************************************************/ +/* + * record the specified amount of ACKs/NAKs + */ +static int rxrpc_call_record_ACK(struct rxrpc_call *call, + struct rxrpc_message *msg, + rxrpc_seq_t seq, + size_t count) +{ + struct rxrpc_message *dmsg; + struct list_head *_p; + rxrpc_seq_t highest; + unsigned ix; + size_t chunk; + char resend, now_complete; + u8 acks[16]; + + _enter("%p{apc=%u ads=%u},%p,%u,%u", + call,call->acks_pend_cnt,call->acks_dftv_seq,msg,seq,count); + + /* handle re-ACK'ing of definitively ACK'd packets (may be out-of-order ACKs) */ + if (seq<=call->acks_dftv_seq) { + unsigned delta = call->acks_dftv_seq - seq; + + if (count<=delta) { + _leave(" = 0 [all definitively ACK'd]"); + return 0; + } + + seq += delta; + count -= delta; + msg->offset += delta; + } + + highest = seq + count - 1; + resend = 0; + while (count>0) { + /* extract up to 16 ACK slots at a time */ + chunk = min(count,sizeof(acks)); + count -= chunk; + + memset(acks,2,sizeof(acks)); + + if (skb_copy_bits(msg->pkt,msg->offset,&acks,chunk)<0) { + printk("Rx Received short ACK packet\n"); + _leave(" = -EINVAL"); + return -EINVAL; + } + msg->offset += chunk; + + /* check that the ACK set is valid */ + for (ix=0; ixacks_pend_cnt + ); + + /* mark the packets in the ACK queue as being provisionally ACK'd */ + ix = 0; + spin_lock(&call->lock); + + /* find the first packet ACK'd/NAK'd here */ + list_for_each(_p,&call->acks_pendq) { + dmsg = list_entry(_p,struct rxrpc_message,link); + if (dmsg->seq==seq) + goto found_first; + _debug("- %u: skipping #%u",ix,dmsg->seq); + } + goto bad_queue; + + found_first: + do { + _debug("- %u: processing #%u (%c) apc=%u", + ix,dmsg->seq,_acktype[acks[ix]],call->acks_pend_cnt); + + if (acks[ix]==RXRPC_ACK_TYPE_ACK) { + if (dmsg->state==RXRPC_MSG_SENT) call->acks_pend_cnt--; + dmsg->state = RXRPC_MSG_ACKED; + } + else { + if (dmsg->state==RXRPC_MSG_ACKED) call->acks_pend_cnt++; + dmsg->state = RXRPC_MSG_SENT; + } + ix++; + seq++; + + _p = dmsg->link.next; + dmsg = list_entry(_p,struct rxrpc_message,link); + } while(ixacks_pendq && dmsg->seq==seq); + + if (ixlock); + } + + if (resend) + rxrpc_call_resend(call,highest); + + /* if all packets are provisionally ACK'd, then wake up anyone who's waiting for that */ + now_complete = 0; + spin_lock(&call->lock); + if (call->acks_pend_cnt==0) { + if (call->app_call_state==RXRPC_CSTATE_SRVR_RCV_FINAL_ACK) { + call->app_call_state = RXRPC_CSTATE_COMPLETE; + _state(call); + } + now_complete = 1; + } + spin_unlock(&call->lock); + + if (now_complete) { + _debug("- wake up waiters"); + del_timer_sync(&call->acks_timeout); + del_timer_sync(&call->rcv_timeout); + del_timer_sync(&call->ackr_dfr_timo); + call->app_attn_func(call); + } + + _leave(" = 0 (apc=%u)",call->acks_pend_cnt); + return 0; + + bad_queue: + panic("%s(): acks_pendq in bad state (packet #%u absent)\n",__FUNCTION__,seq); + +} /* end rxrpc_call_record_ACK() */ + +/*****************************************************************************/ +/* + * transfer data from the ready packet queue to the asynchronous read buffer + * - since this func is the only one going to look at packets queued on app_readyq, we don't need + * a lock to modify or access them, only to modify the queue pointers + * - called with call->lock held + * - the buffer must be in kernel space + * - returns: + * 0 if buffer filled + * -EAGAIN if buffer not filled and more data to come + * -EBADMSG if last packet received and insufficient data left + * -ECONNABORTED if the call has in an error state + */ +static int __rxrpc_call_read_data(struct rxrpc_call *call) +{ + struct rxrpc_message *msg; + size_t qty; + int ret; + + _enter("%p{as=%d buf=%p qty=%u/%u}", + call,call->app_async_read,call->app_read_buf,call->app_ready_qty,call->app_mark); + + /* check the state */ + switch (call->app_call_state) { + case RXRPC_CSTATE_SRVR_RCV_ARGS: + case RXRPC_CSTATE_CLNT_RCV_REPLY: + if (call->app_last_rcv) { + printk("%s(%p,%p,%d): Inconsistent call state (%s, last pkt)", + __FUNCTION__,call,call->app_read_buf,call->app_mark, + rxrpc_call_states[call->app_call_state]); + BUG(); + } + break; + + case RXRPC_CSTATE_SRVR_RCV_OPID: + case RXRPC_CSTATE_SRVR_GOT_ARGS: + case RXRPC_CSTATE_CLNT_GOT_REPLY: + break; + + case RXRPC_CSTATE_SRVR_SND_REPLY: + if (!call->app_last_rcv) { + printk("%s(%p,%p,%d): Inconsistent call state (%s, not last pkt)", + __FUNCTION__,call,call->app_read_buf,call->app_mark, + rxrpc_call_states[call->app_call_state]); + BUG(); + } + _debug("Trying to read data from call in SND_REPLY state"); + break; + + case RXRPC_CSTATE_ERROR: + _leave(" = -ECONNABORTED"); + return -ECONNABORTED; + + default: + printk("reading in unexpected state [[[ %u ]]]\n",call->app_call_state); + BUG(); + } + + /* handle the case of not having an async buffer */ + if (!call->app_async_read) { + if (call->app_mark==RXRPC_APP_MARK_EOF) { + ret = call->app_last_rcv ? 0 : -EAGAIN; + } + else { + if (call->app_mark >= call->app_ready_qty) { + call->app_mark = RXRPC_APP_MARK_EOF; + ret = 0; + } + else { + ret = call->app_last_rcv ? -EBADMSG : -EAGAIN; + } + } + + _leave(" = %d [no buf]",ret); + return 0; + } + + while (!list_empty(&call->app_readyq) && call->app_mark>0) { + msg = list_entry(call->app_readyq.next,struct rxrpc_message,link); + + /* drag as much data as we need out of this packet */ + qty = min(call->app_mark,msg->dsize); + + _debug("reading %u from skb=%p off=%lu",qty,msg->pkt,msg->offset); + + if (call->app_read_buf) + if (skb_copy_bits(msg->pkt,msg->offset,call->app_read_buf,qty)<0) + panic("%s: Failed to copy data from packet: (%p,%p,%d)", + __FUNCTION__,call,call->app_read_buf,qty); + + /* if that packet is now empty, discard it */ + call->app_ready_qty -= qty; + msg->dsize -= qty; + + if (msg->dsize==0) { + list_del_init(&msg->link); + rxrpc_put_message(msg); + } + else { + msg->offset += qty; + } + + call->app_mark -= qty; + if (call->app_read_buf) call->app_read_buf += qty; + } + + if (call->app_mark==0) { + call->app_async_read = 0; + call->app_mark = RXRPC_APP_MARK_EOF; + call->app_read_buf = NULL; + + /* adjust the state if used up all packets */ + if (list_empty(&call->app_readyq) && call->app_last_rcv) { + switch (call->app_call_state) { + case RXRPC_CSTATE_SRVR_RCV_OPID: + call->app_call_state = RXRPC_CSTATE_SRVR_SND_REPLY; + call->app_mark = RXRPC_APP_MARK_EOF; + _state(call); + del_timer_sync(&call->rcv_timeout); + break; + case RXRPC_CSTATE_SRVR_GOT_ARGS: + call->app_call_state = RXRPC_CSTATE_SRVR_SND_REPLY; + _state(call); + del_timer_sync(&call->rcv_timeout); + break; + default: + call->app_call_state = RXRPC_CSTATE_COMPLETE; + _state(call); + del_timer_sync(&call->acks_timeout); + del_timer_sync(&call->ackr_dfr_timo); + del_timer_sync(&call->rcv_timeout); + break; + } + } + + _leave(" = 0"); + return 0; + } + + if (call->app_last_rcv) { + _debug("Insufficient data (%u/%u)",call->app_ready_qty,call->app_mark); + call->app_async_read = 0; + call->app_mark = RXRPC_APP_MARK_EOF; + call->app_read_buf = NULL; + + _leave(" = -EBADMSG"); + return -EBADMSG; + } + + _leave(" = -EAGAIN"); + return -EAGAIN; +} /* end __rxrpc_call_read_data() */ + +/*****************************************************************************/ +/* + * attempt to read the specified amount of data from the call's ready queue into the buffer + * provided + * - since this func is the only one going to look at packets queued on app_readyq, we don't need + * a lock to modify or access them, only to modify the queue pointers + * - if the buffer pointer is NULL, then data is merely drained, not copied + * - if flags&RXRPC_CALL_READ_BLOCK, then the function will wait until there is enough data or an + * error will be generated + * - note that the caller must have added the calling task to the call's wait queue beforehand + * - if flags&RXRPC_CALL_READ_ALL, then an error will be generated if this function doesn't read + * all available data + */ +int rxrpc_call_read_data(struct rxrpc_call *call, void *buffer, size_t size, int flags) +{ + int ret; + + _enter("%p{arq=%u},%p,%d,%x",call,call->app_ready_qty,buffer,size,flags); + + spin_lock(&call->lock); + + if (unlikely(!!call->app_read_buf)) { + spin_unlock(&call->lock); + _leave(" = -EBUSY"); + return -EBUSY; + } + + call->app_mark = size; + call->app_read_buf = buffer; + call->app_async_read = 1; + call->app_read_count++; + + /* read as much data as possible */ + ret = __rxrpc_call_read_data(call); + switch (ret) { + case 0: + if (flags&RXRPC_CALL_READ_ALL && (!call->app_last_rcv || call->app_ready_qty>0)) { + _leave(" = -EBADMSG"); + __rxrpc_call_abort(call,-EBADMSG); + return -EBADMSG; + } + + spin_unlock(&call->lock); + call->app_attn_func(call); + _leave(" = 0"); + return ret; + + case -ECONNABORTED: + spin_unlock(&call->lock); + _leave(" = %d [aborted]",ret); + return ret; + + default: + __rxrpc_call_abort(call,ret); + _leave(" = %d",ret); + return ret; + + case -EAGAIN: + spin_unlock(&call->lock); + + if (!(flags&RXRPC_CALL_READ_BLOCK)) { + _leave(" = -EAGAIN"); + return -EAGAIN; + } + + /* wait for the data to arrive */ + _debug("blocking for data arrival"); + + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (!call->app_async_read || signal_pending(current)) + break; + schedule(); + } + set_current_state(TASK_RUNNING); + + if (signal_pending(current)) { + _leave(" = -EINTR"); + return -EINTR; + } + + if (call->app_call_state==RXRPC_CSTATE_ERROR) { + _leave(" = -ECONNABORTED"); + return -ECONNABORTED; + } + + _leave(" = 0"); + return 0; + } + +} /* end rxrpc_call_read_data() */ + +/*****************************************************************************/ +/* + * write data to a call + * - the data may not be sent immediately if it doesn't fill a buffer + * - if we can't queue all the data for buffering now, siov[] will have been adjusted to take + * account of what has been sent + */ +int rxrpc_call_write_data(struct rxrpc_call *call, + size_t sioc, + struct iovec siov[], + u8 rxhdr_flags, + int alloc_flags, + int dup_data, + size_t *size_sent) +{ + struct rxrpc_message *msg; + struct iovec *sptr; + size_t space, size, chunk, tmp; + char *buf; + int ret; + + _enter("%p,%u,%p,%02x,%x,%d,%p",call,sioc,siov,rxhdr_flags,alloc_flags,dup_data,size_sent); + + *size_sent = 0; + size = 0; + ret = -EINVAL; + + /* can't send more if we've sent last packet from this end */ + switch (call->app_call_state) { + case RXRPC_CSTATE_SRVR_SND_REPLY: + case RXRPC_CSTATE_CLNT_SND_ARGS: + break; + case RXRPC_CSTATE_ERROR: + ret = call->app_errno; + default: + goto out; + } + + /* calculate how much data we've been given */ + sptr = siov; + for (; sioc>0; sptr++, sioc--) { + if (!sptr->iov_len) continue; + + if (!sptr->iov_base) + goto out; + + size += sptr->iov_len; + } + + _debug("- size=%u mtu=%u",size,call->conn->mtu_size); + + do { + /* make sure there's a message under construction */ + if (!call->snd_nextmsg) { + /* no - allocate a message with no data yet attached */ + ret = rxrpc_conn_newmsg(call->conn,call,RXRPC_PACKET_TYPE_DATA, + 0,NULL,alloc_flags,&call->snd_nextmsg); + if (ret<0) + goto out; + _debug("- allocated new message [ds=%u]",call->snd_nextmsg->dsize); + } + + msg = call->snd_nextmsg; + msg->hdr.flags |= rxhdr_flags; + + /* deal with zero-length terminal packet */ + if (size==0) { + if (rxhdr_flags & RXRPC_LAST_PACKET) { + ret = rxrpc_call_flush(call); + if (ret<0) + goto out; + } + break; + } + + /* work out how much space current packet has available */ + space = call->conn->mtu_size - msg->dsize; + chunk = min(space,size); + + _debug("- [before] space=%u chunk=%u",space,chunk); + + while (!siov->iov_len) + siov++; + + /* if we are going to have to duplicate the data then coalesce it too */ + if (dup_data) { + /* don't allocate more that 1 page at a time */ + if (chunk>PAGE_SIZE) + chunk = PAGE_SIZE; + + /* allocate a data buffer and attach to the message */ + buf = kmalloc(chunk,alloc_flags); + if (unlikely(!buf)) { + if (msg->dsize==sizeof(struct rxrpc_header)) { + /* discard an empty msg and wind back the seq counter */ + rxrpc_put_message(msg); + call->snd_nextmsg = NULL; + call->snd_seq_count--; + } + + ret = -ENOMEM; + goto out; + } + + tmp = msg->dcount++; + set_bit(tmp,&msg->dfree); + msg->data[tmp].iov_base = buf; + msg->data[tmp].iov_len = chunk; + msg->dsize += chunk; + *size_sent += chunk; + size -= chunk; + + /* load the buffer with data */ + while (chunk>0) { + tmp = min(chunk,siov->iov_len); + memcpy(buf,siov->iov_base,tmp); + buf += tmp; + siov->iov_base += tmp; + siov->iov_len -= tmp; + if (!siov->iov_len) + siov++; + chunk -= tmp; + } + } + else { + /* we want to attach the supplied buffers directly */ + while (chunk>0 && msg->dcountdcount++; + msg->data[tmp].iov_base = siov->iov_base; + msg->data[tmp].iov_len = siov->iov_len; + msg->dsize += siov->iov_len; + *size_sent += siov->iov_len; + size -= siov->iov_len; + chunk -= siov->iov_len; + siov++; + } + } + + _debug("- [loaded] chunk=%u size=%u",chunk,size); + + /* dispatch the message when full, final or requesting ACK */ + if (msg->dsize>=call->conn->mtu_size || rxhdr_flags) { + ret = rxrpc_call_flush(call); + if (ret<0) + goto out; + } + + } while(size>0); + + ret = 0; + out: + _leave(" = %d (%d queued, %d rem)",ret,*size_sent,size); + return ret; + +} /* end rxrpc_call_write_data() */ + +/*****************************************************************************/ +/* + * flush outstanding packets to the network + */ +int rxrpc_call_flush(struct rxrpc_call *call) +{ + struct rxrpc_message *msg; + int ret = 0; + + _enter("%p",call); + + rxrpc_get_call(call); + + /* if there's a packet under construction, then dispatch it now */ + if (call->snd_nextmsg) { + msg = call->snd_nextmsg; + call->snd_nextmsg = NULL; + + if (msg->hdr.flags & RXRPC_LAST_PACKET) { + msg->hdr.flags &= ~RXRPC_MORE_PACKETS; + msg->hdr.flags |= RXRPC_REQUEST_ACK; + } + else { + msg->hdr.flags |= RXRPC_MORE_PACKETS; + } + + _proto("Sending DATA message { ds=%u dc=%u df=%02lu }", + msg->dsize,msg->dcount,msg->dfree); + + /* queue and adjust call state */ + spin_lock(&call->lock); + list_add_tail(&msg->link,&call->acks_pendq); + + /* decide what to do depending on current state and if this is the last packet */ + ret = -EINVAL; + switch (call->app_call_state) { + case RXRPC_CSTATE_SRVR_SND_REPLY: + if (msg->hdr.flags & RXRPC_LAST_PACKET) { + call->app_call_state = RXRPC_CSTATE_SRVR_RCV_FINAL_ACK; + _state(call); + } + break; + + case RXRPC_CSTATE_CLNT_SND_ARGS: + if (msg->hdr.flags & RXRPC_LAST_PACKET) { + call->app_call_state = RXRPC_CSTATE_CLNT_RCV_REPLY; + _state(call); + } + break; + + case RXRPC_CSTATE_ERROR: + ret = call->app_errno; + default: + spin_unlock(&call->lock); + goto out; + } + + call->acks_pend_cnt++; + + mod_timer(&call->acks_timeout,jiffies + rxrpc_call_acks_timeout); + + spin_unlock(&call->lock); + + ret = rxrpc_conn_sendmsg(call->conn,msg); + if (ret==0) + call->pkt_snd_count++; + } + + out: + rxrpc_put_call(call); + + _leave(" = %d",ret); + return ret; + +} /* end rxrpc_call_flush() */ + +/*****************************************************************************/ +/* + * resend NAK'd or unacknowledged packets up to the highest one specified + */ +static void rxrpc_call_resend(struct rxrpc_call *call, rxrpc_seq_t highest) +{ + struct rxrpc_message *msg; + struct list_head *_p; + rxrpc_seq_t seq = 0; + + _enter("%p,%u",call,highest); + + _proto("Rx Resend required"); + + /* handle too many resends */ + if (call->snd_resend_cnt>=rxrpc_call_max_resend) { + _debug("Aborting due to too many resends (rcv=%d)",call->pkt_rcv_count); + rxrpc_call_abort(call,call->pkt_rcv_count>0?-EIO:-ETIMEDOUT); + _leave(""); + return; + } + + spin_lock(&call->lock); + call->snd_resend_cnt++; + for (;;) { + /* determine which the next packet we might need to ACK is */ + if (seq<=call->acks_dftv_seq) + seq = call->acks_dftv_seq; + seq++; + + if (seq>highest) + break; + + /* look for the packet in the pending-ACK queue */ + list_for_each(_p,&call->acks_pendq) { + msg = list_entry(_p,struct rxrpc_message,link); + if (msg->seq==seq) + goto found_msg; + } + + panic("%s(%p,%d): Inconsistent pending-ACK queue (ds=%u sc=%u sq=%u)\n", + __FUNCTION__,call,highest,call->acks_dftv_seq,call->snd_seq_count,seq); + + found_msg: + if (msg->state!=RXRPC_MSG_SENT) + continue; /* only un-ACK'd packets */ + + rxrpc_get_message(msg); + spin_unlock(&call->lock); + + /* send each message again (and ignore any errors we might incur) */ + _proto("Resending DATA message { ds=%u dc=%u df=%02lu }", + msg->dsize,msg->dcount,msg->dfree); + + if (rxrpc_conn_sendmsg(call->conn,msg)==0) + call->pkt_snd_count++; + + rxrpc_put_message(msg); + + spin_lock(&call->lock); + } + + /* reset the timeout */ + mod_timer(&call->acks_timeout,jiffies + rxrpc_call_acks_timeout); + + spin_unlock(&call->lock); + + _leave(""); +} /* end rxrpc_call_resend() */ + +/*****************************************************************************/ +/* + * handle an ICMP error being applied to a call + */ +void rxrpc_call_handle_error(struct rxrpc_call *call, int local, int errno) +{ + _enter("%p{%u},%d",call,ntohl(call->call_id),errno); + + /* if this call is already aborted, then just wake up any waiters */ + if (call->app_call_state==RXRPC_CSTATE_ERROR) { + call->app_error_func(call); + } + else { + /* tell the app layer what happened */ + spin_lock(&call->lock); + call->app_call_state = RXRPC_CSTATE_ERROR; + _state(call); + if (local) + call->app_err_state = RXRPC_ESTATE_LOCAL_ERROR; + else + call->app_err_state = RXRPC_ESTATE_REMOTE_ERROR; + call->app_errno = errno; + call->app_mark = RXRPC_APP_MARK_EOF; + call->app_read_buf = NULL; + call->app_async_read = 0; + + /* map the error */ + call->app_aemap_func(call); + + del_timer_sync(&call->acks_timeout); + del_timer_sync(&call->rcv_timeout); + del_timer_sync(&call->ackr_dfr_timo); + + spin_unlock(&call->lock); + + call->app_error_func(call); + } + + _leave(""); +} /* end rxrpc_call_handle_error() */ diff --git a/net/rxrpc/connection.c b/net/rxrpc/connection.c new file mode 100644 index 000000000000..e54dd472b5e4 --- /dev/null +++ b/net/rxrpc/connection.c @@ -0,0 +1,687 @@ +/* connection.c: Rx connection routines + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +__RXACCT_DECL(atomic_t rxrpc_connection_count); + +LIST_HEAD(rxrpc_conns); +DECLARE_RWSEM(rxrpc_conns_sem); + +static void __rxrpc_conn_timeout(rxrpc_timer_t *timer) +{ + struct rxrpc_connection *conn = list_entry(timer,struct rxrpc_connection,timeout); + + _debug("Rx CONN TIMEOUT [%p{u=%d}]",conn,atomic_read(&conn->usage)); + + rxrpc_conn_do_timeout(conn); +} + +static const struct rxrpc_timer_ops rxrpc_conn_timer_ops = { + timed_out: __rxrpc_conn_timeout, +}; + +/*****************************************************************************/ +/* + * create a new connection record + */ +static inline int __rxrpc_create_connection(struct rxrpc_peer *peer, + struct rxrpc_connection **_conn) +{ + struct rxrpc_connection *conn; + + _enter("%p",peer); + + /* allocate and initialise a connection record */ + conn = kmalloc(sizeof(struct rxrpc_connection),GFP_KERNEL); + if (!conn) { + _leave(" = -ENOMEM"); + return -ENOMEM; + } + + memset(conn,0,sizeof(struct rxrpc_connection)); + atomic_set(&conn->usage,1); + + INIT_LIST_HEAD(&conn->link); + init_waitqueue_head(&conn->chanwait); + spin_lock_init(&conn->lock); + rxrpc_timer_init(&conn->timeout,&rxrpc_conn_timer_ops); + + do_gettimeofday(&conn->atime); + conn->mtu_size = 1024; + conn->peer = peer; + conn->trans = peer->trans; + + __RXACCT(atomic_inc(&rxrpc_connection_count)); + *_conn = conn; + _leave(" = 0 (%p)",conn); + + return 0; +} /* end __rxrpc_create_connection() */ + +/*****************************************************************************/ +/* + * create a new connection record for outgoing connections + */ +int rxrpc_create_connection(struct rxrpc_transport *trans, + u16 port, + u32 addr, + unsigned short service_id, + void *security, + struct rxrpc_connection **_conn) +{ + struct rxrpc_connection *conn; + struct rxrpc_peer *peer; + int ret; + + _enter("%p{%hu},%u,%hu",trans,trans->port,ntohs(port),service_id); + + /* get a peer record */ + ret = rxrpc_peer_lookup(trans,addr,&peer); + if (ret<0) { + _leave(" = %d",ret); + return ret; + } + + /* allocate and initialise a connection record */ + ret = __rxrpc_create_connection(peer,&conn); + if (ret<0) { + rxrpc_put_peer(peer); + _leave(" = %d",ret); + return ret; + } + + /* fill in the specific bits */ + conn->addr.sin_family = AF_INET; + conn->addr.sin_port = port; + conn->addr.sin_addr.s_addr = addr; + + conn->in_epoch = rxrpc_epoch; + conn->out_epoch = rxrpc_epoch; + conn->in_clientflag = 0; + conn->out_clientflag = RXRPC_CLIENT_INITIATED; + conn->conn_id = htonl((unsigned) conn & RXRPC_CIDMASK); + conn->service_id = htons(service_id); + + /* attach to peer */ + conn->peer = peer; + + write_lock(&peer->conn_lock); + list_add_tail(&conn->link,&peer->conn_active); + atomic_inc(&peer->conn_count); + write_unlock(&peer->conn_lock); + + down_write(&rxrpc_conns_sem); + list_add_tail(&conn->proc_link,&rxrpc_conns); + up_write(&rxrpc_conns_sem); + + *_conn = conn; + _leave(" = 0 (%p)",conn); + + return 0; +} /* end rxrpc_create_connection() */ + +/*****************************************************************************/ +/* + * lookup the connection for an incoming packet + * - create a new connection record for unrecorded incoming connections + */ +int rxrpc_connection_lookup(struct rxrpc_peer *peer, + struct rxrpc_message *msg, + struct rxrpc_connection **_conn) +{ + struct rxrpc_connection *conn, *candidate = NULL; + struct list_head *_p; + int ret, fresh = 0; + u32 x_epoch, x_connid; + u16 x_port, x_secix, x_servid; + u8 x_clflag; + + _enter("%p{{%hu}},%u,%hu", + peer,peer->trans->port,ntohs(msg->pkt->h.uh->source),ntohs(msg->hdr.serviceId)); + + x_port = msg->pkt->h.uh->source; + x_epoch = msg->hdr.epoch; + x_clflag = msg->hdr.flags & RXRPC_CLIENT_INITIATED; + x_connid = htonl(ntohl(msg->hdr.cid) & RXRPC_CIDMASK); + x_servid = msg->hdr.serviceId; + x_secix = msg->hdr.securityIndex; + + /* [common case] search the transport's active list first */ + read_lock(&peer->conn_lock); + list_for_each(_p,&peer->conn_active) { + conn = list_entry(_p,struct rxrpc_connection,link); + if (conn->addr.sin_port == x_port && + conn->in_epoch == x_epoch && + conn->conn_id == x_connid && + conn->security_ix == x_secix && + conn->service_id == x_servid && + conn->in_clientflag == x_clflag) + goto found_active; + } + read_unlock(&peer->conn_lock); + + /* [uncommon case] not active + * - create a candidate for a new record if an inbound connection + * - only examine the graveyard for an outbound connection + */ + if (x_clflag) { + ret = __rxrpc_create_connection(peer,&candidate); + if (ret<0) { + _leave(" = %d",ret); + return ret; + } + + /* fill in the specifics */ + candidate->addr.sin_family = AF_INET; + candidate->addr.sin_port = x_port; + candidate->addr.sin_addr.s_addr = msg->pkt->nh.iph->saddr; + candidate->in_epoch = x_epoch; + candidate->out_epoch = x_epoch; + candidate->in_clientflag = RXRPC_CLIENT_INITIATED; + candidate->out_clientflag = 0; + candidate->conn_id = x_connid; + candidate->service_id = x_servid; + candidate->security_ix = x_secix; + } + + /* search the active list again, just in case it appeared whilst we were busy */ + write_lock(&peer->conn_lock); + list_for_each(_p,&peer->conn_active) { + conn = list_entry(_p,struct rxrpc_connection,link); + if (conn->addr.sin_port == x_port && + conn->in_epoch == x_epoch && + conn->conn_id == x_connid && + conn->security_ix == x_secix && + conn->service_id == x_servid && + conn->in_clientflag == x_clflag) + goto found_active_second_chance; + } + + /* search the transport's graveyard list */ + spin_lock(&peer->conn_gylock); + list_for_each(_p,&peer->conn_graveyard) { + conn = list_entry(_p,struct rxrpc_connection,link); + if (conn->addr.sin_port == x_port && + conn->in_epoch == x_epoch && + conn->conn_id == x_connid && + conn->security_ix == x_secix && + conn->service_id == x_servid && + conn->in_clientflag == x_clflag) + goto found_in_graveyard; + } + spin_unlock(&peer->conn_gylock); + + /* outbound connections aren't created here */ + if (!x_clflag) { + write_unlock(&peer->conn_lock); + _leave(" = -ENOENT"); + return -ENOENT; + } + + /* we can now add the new candidate to the list */ + rxrpc_get_peer(peer); + conn = candidate; + candidate = NULL; + atomic_inc(&peer->conn_count); + fresh = 1; + + make_active: + list_add_tail(&conn->link,&peer->conn_active); + + success_uwfree: + write_unlock(&peer->conn_lock); + + if (candidate) { + __RXACCT(atomic_dec(&rxrpc_connection_count)); + kfree(candidate); + } + + if (fresh) { + down_write(&rxrpc_conns_sem); + list_add_tail(&conn->proc_link,&rxrpc_conns); + up_write(&rxrpc_conns_sem); + } + + success: + *_conn = conn; + _leave(" = 0 (%p)",conn); + return 0; + + /* handle the connection being found in the active list straight off */ + found_active: + rxrpc_get_connection(conn); + read_unlock(&peer->conn_lock); + goto success; + + /* handle resurrecting a connection from the graveyard */ + found_in_graveyard: + rxrpc_get_peer(peer); + rxrpc_get_connection(conn); + rxrpc_krxtimod_del_timer(&conn->timeout); + list_del_init(&conn->link); + spin_unlock(&peer->conn_gylock); + goto make_active; + + /* handle finding the connection on the second time through the active list */ + found_active_second_chance: + rxrpc_get_connection(conn); + goto success_uwfree; + +} /* end rxrpc_connection_lookup() */ + +/*****************************************************************************/ +/* + * finish using a connection record + * - it will be transferred to the peer's connection graveyard when refcount reaches 0 + */ +void rxrpc_put_connection(struct rxrpc_connection *conn) +{ + struct rxrpc_peer *peer = conn->peer; + + _enter("%p{u=%d p=%hu}",conn,atomic_read(&conn->usage),ntohs(conn->addr.sin_port)); + + /* sanity check */ + if (atomic_read(&conn->usage)<=0) + BUG(); + + spin_lock(&peer->conn_gylock); + if (likely(!atomic_dec_and_test(&conn->usage))) { + spin_unlock(&peer->conn_gylock); + _leave(""); + return; + } + + /* move to graveyard queue */ + list_del(&conn->link); + list_add_tail(&conn->link,&peer->conn_graveyard); + + /* discard in 100 secs */ + rxrpc_krxtimod_add_timer(&conn->timeout,20*HZ); + + spin_unlock(&peer->conn_gylock); + + rxrpc_put_peer(conn->peer); + + _leave(" [killed]"); +} /* end rxrpc_put_connection() */ + +/*****************************************************************************/ +/* + * free a connection record + */ +void rxrpc_conn_do_timeout(struct rxrpc_connection *conn) +{ + struct rxrpc_peer *peer; + + _enter("%p{u=%d p=%hu}",conn,atomic_read(&conn->usage),ntohs(conn->addr.sin_port)); + + peer = conn->peer; + + if (atomic_read(&conn->usage)<0) + BUG(); + + /* remove from graveyard if still dead */ + spin_lock(&peer->conn_gylock); + if (atomic_read(&conn->usage)==0) { + list_del_init(&conn->link); + } + else { + conn = NULL; + } + spin_unlock(&peer->conn_gylock); + + if (!conn) { + _leave(""); + return; /* resurrected */ + } + + _debug("--- Destroying Connection %p ---",conn); + + down_write(&rxrpc_conns_sem); + list_del(&conn->proc_link); + up_write(&rxrpc_conns_sem); + + __RXACCT(atomic_dec(&rxrpc_connection_count)); + kfree(conn); + + /* if the graveyard is now empty, wake up anyone waiting for that */ + if (atomic_dec_and_test(&peer->conn_count)) + wake_up(&peer->conn_gy_waitq); + + _leave(" [destroyed]"); +} /* end rxrpc_conn_do_timeout() */ + +/*****************************************************************************/ +/* + * clear all connection records from a peer endpoint + */ +void rxrpc_conn_clearall(struct rxrpc_peer *peer) +{ + DECLARE_WAITQUEUE(myself,current); + + struct rxrpc_connection *conn; + int err; + + _enter("%p",peer); + + /* there shouldn't be any active conns remaining */ + if (!list_empty(&peer->conn_active)) + BUG(); + + /* manually timeout all conns in the graveyard */ + spin_lock(&peer->conn_gylock); + while (!list_empty(&peer->conn_graveyard)) { + conn = list_entry(peer->conn_graveyard.next,struct rxrpc_connection,link); + err = rxrpc_krxtimod_del_timer(&conn->timeout); + spin_unlock(&peer->conn_gylock); + + if (err==0) + rxrpc_conn_do_timeout(conn); + + spin_lock(&peer->conn_gylock); + } + spin_unlock(&peer->conn_gylock); + + /* wait for the the conn graveyard to be completely cleared */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&peer->conn_gy_waitq,&myself); + + while (atomic_read(&peer->conn_count)!=0) { + schedule(); + set_current_state(TASK_UNINTERRUPTIBLE); + } + + remove_wait_queue(&peer->conn_gy_waitq,&myself); + set_current_state(TASK_RUNNING); + + _leave(""); + +} /* end rxrpc_conn_clearall() */ + +/*****************************************************************************/ +/* + * allocate and prepare a message for sending out through the transport endpoint + */ +int rxrpc_conn_newmsg(struct rxrpc_connection *conn, + struct rxrpc_call *call, + u8 type, + int dcount, + struct iovec diov[], + int alloc_flags, + struct rxrpc_message **_msg) +{ + struct rxrpc_message *msg; + int loop; + + _enter("%p{%d},%p,%u",conn,ntohs(conn->addr.sin_port),call,type); + + if (dcount>3) { + _leave(" = -EINVAL"); + return -EINVAL; + } + + msg = kmalloc(sizeof(struct rxrpc_message),alloc_flags); + if (!msg) { + _leave(" = -ENOMEM"); + return -ENOMEM; + } + + memset(msg,0,sizeof(*msg)); + atomic_set(&msg->usage,1); + + INIT_LIST_HEAD(&msg->link); + + msg->state = RXRPC_MSG_PREPARED; + + msg->hdr.epoch = conn->out_epoch; + msg->hdr.cid = conn->conn_id | (call ? call->chan_ix : 0); + msg->hdr.callNumber = call ? call->call_id : 0; + msg->hdr.type = type; + msg->hdr.flags = conn->out_clientflag; + msg->hdr.securityIndex = conn->security_ix; + msg->hdr.serviceId = conn->service_id; + + /* generate sequence numbers for data packets */ + if (call) { + switch (type) { + case RXRPC_PACKET_TYPE_DATA: + msg->seq = ++call->snd_seq_count; + msg->hdr.seq = htonl(msg->seq); + break; + case RXRPC_PACKET_TYPE_ACK: + /* ACK sequence numbers are complicated. The following may be wrong: + * - jumbo packet ACKs should have a seq number + * - normal ACKs should not + */ + default: + break; + } + } + + msg->dcount = dcount + 1; + msg->dsize = sizeof(msg->hdr); + msg->data[0].iov_len = sizeof(msg->hdr); + msg->data[0].iov_base = &msg->hdr; + + for (loop=0; loopdsize += diov[loop].iov_len; + msg->data[loop+1].iov_len = diov[loop].iov_len; + msg->data[loop+1].iov_base = diov[loop].iov_base; + } + + __RXACCT(atomic_inc(&rxrpc_message_count)); + *_msg = msg; + _leave(" = 0 (%p) #%d",msg,atomic_read(&rxrpc_message_count)); + return 0; +} /* end rxrpc_conn_newmsg() */ + +/*****************************************************************************/ +/* + * free a message + */ +void __rxrpc_put_message(struct rxrpc_message *msg) +{ + int loop; + + _enter("%p #%d",msg,atomic_read(&rxrpc_message_count)); + + if (msg->pkt) kfree_skb(msg->pkt); + if (msg->conn) rxrpc_put_connection(msg->conn); + + for (loop=0; loop<8; loop++) + if (test_bit(loop,&msg->dfree)) + kfree(msg->data[loop].iov_base); + + __RXACCT(atomic_dec(&rxrpc_message_count)); + kfree(msg); + + _leave(""); +} /* end __rxrpc_put_message() */ + +/*****************************************************************************/ +/* + * send a message out through the transport endpoint + */ +int rxrpc_conn_sendmsg(struct rxrpc_connection *conn, struct rxrpc_message *msg) +{ + struct msghdr msghdr; + mm_segment_t oldfs; + int ret; + + _enter("%p{%d}",conn,ntohs(conn->addr.sin_port)); + + /* fill in some fields in the header */ + spin_lock(&conn->lock); + msg->hdr.serial = htonl(++conn->serial_counter); + msg->rttdone = 0; + spin_unlock(&conn->lock); + + /* set up the message to be transmitted */ + msghdr.msg_name = &conn->addr; + msghdr.msg_namelen = sizeof(conn->addr); + msghdr.msg_iov = msg->data; + msghdr.msg_iovlen = msg->dcount; + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + msghdr.msg_flags = MSG_CONFIRM|MSG_DONTWAIT; + + _net("Sending message type %d of %d bytes to %08x:%d", + msg->hdr.type, + msg->dsize, + htonl(conn->addr.sin_addr.s_addr), + htons(conn->addr.sin_port)); + + /* send the message */ + oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = sock_sendmsg(conn->trans->socket,&msghdr,msg->dsize); + set_fs(oldfs); + + if (ret<0) { + msg->state = RXRPC_MSG_ERROR; + } + else { + msg->state = RXRPC_MSG_SENT; + ret = 0; + + spin_lock(&conn->lock); + do_gettimeofday(&conn->atime); + msg->stamp = conn->atime; + spin_unlock(&conn->lock); + } + + _leave(" = %d",ret); + + return ret; +} /* end rxrpc_conn_sendmsg() */ + +/*****************************************************************************/ +/* + * deal with a subsequent call packet + */ +int rxrpc_conn_receive_call_packet(struct rxrpc_connection *conn, + struct rxrpc_call *call, + struct rxrpc_message *msg) +{ + struct rxrpc_message *pmsg; + struct list_head *_p; + unsigned cix, seq; + int ret = 0; + + _enter("%p,%p,%p",conn,call,msg); + + if (!call) { + cix = ntohl(msg->hdr.cid) & RXRPC_CHANNELMASK; + + spin_lock(&conn->lock); + call = conn->channels[cix]; + + if (!call || call->call_id != msg->hdr.callNumber) { + spin_unlock(&conn->lock); + rxrpc_trans_immediate_abort(conn->trans,msg,-ENOENT); + goto out; + } + else { + rxrpc_get_call(call); + spin_unlock(&conn->lock); + } + } + else { + rxrpc_get_call(call); + } + + _proto("Received packet %%%u [%u] on call %hu:%u:%u", + htonl(msg->hdr.serial), + htonl(msg->hdr.seq), + htons(msg->hdr.serviceId), + htonl(conn->conn_id), + htonl(call->call_id)); + + call->pkt_rcv_count++; + + if (msg->pkt->dst && msg->pkt->dst->dev) + conn->peer->if_mtu = msg->pkt->dst->dev->mtu - msg->pkt->dst->dev->hard_header_len; + + /* queue on the call in seq order */ + rxrpc_get_message(msg); + seq = msg->seq; + + spin_lock(&call->lock); + list_for_each(_p,&call->rcv_receiveq) { + pmsg = list_entry(_p,struct rxrpc_message,link); + if (pmsg->seq>seq) + break; + } + list_add_tail(&msg->link,_p); + + /* reset the activity timeout */ + call->flags |= RXRPC_CALL_RCV_PKT; + mod_timer(&call->rcv_timeout,jiffies + rxrpc_call_rcv_timeout * HZ); + + spin_unlock(&call->lock); + + rxrpc_krxiod_queue_call(call); + + rxrpc_put_call(call); + out: + _leave(" = %d",ret); + + return ret; +} /* end rxrpc_conn_receive_call_packet() */ + +/*****************************************************************************/ +/* + * handle an ICMP error being applied to a connection + */ +void rxrpc_conn_handle_error(struct rxrpc_connection *conn, int local, int errno) +{ + struct rxrpc_call *calls[4]; + int loop; + + _enter("%p{%d},%d",conn,ntohs(conn->addr.sin_port),errno); + + /* get a ref to all my calls in one go */ + memset(calls,0,sizeof(calls)); + spin_lock(&conn->lock); + + for (loop=3; loop>=0; loop--) { + if (conn->channels[loop]) { + calls[loop] = conn->channels[loop]; + rxrpc_get_call(calls[loop]); + } + } + + spin_unlock(&conn->lock); + + /* now kick them all */ + for (loop=3; loop>=0; loop--) { + if (calls[loop]) { + rxrpc_call_handle_error(calls[loop],local,errno); + rxrpc_put_call(calls[loop]); + } + } + + _leave(""); +} /* end rxrpc_conn_handle_error() */ diff --git a/net/rxrpc/internal.h b/net/rxrpc/internal.h new file mode 100644 index 000000000000..afd712a439f9 --- /dev/null +++ b/net/rxrpc/internal.h @@ -0,0 +1,107 @@ +/* internal.h: internal Rx RPC stuff + * + * Copyright (c) 2002 David Howells (dhowells@redhat.com). + */ + +#ifndef RXRPC_INTERNAL_H +#define RXRPC_INTERNAL_H + +#include +#include + +/* + * debug accounting + */ +#if 1 +#define __RXACCT_DECL(X) X +#define __RXACCT(X) do { X; } while(0) +#else +#define __RXACCT_DECL(X) +#define __RXACCT(X) do { } while(0) +#endif + +__RXACCT_DECL(extern atomic_t rxrpc_transport_count); +__RXACCT_DECL(extern atomic_t rxrpc_peer_count); +__RXACCT_DECL(extern atomic_t rxrpc_connection_count); +__RXACCT_DECL(extern atomic_t rxrpc_call_count); +__RXACCT_DECL(extern atomic_t rxrpc_message_count); + +/* + * debug tracing + */ +#define kenter(FMT,...) printk("==> %s("FMT")\n",__FUNCTION__,##__VA_ARGS__) +#define kleave(FMT,...) printk("<== %s()"FMT"\n",__FUNCTION__,##__VA_ARGS__) +#define kdebug(FMT,...) printk(" "FMT"\n",##__VA_ARGS__) +#define kproto(FMT,...) printk("### "FMT"\n",##__VA_ARGS__) +#define knet(FMT,...) printk(" "FMT"\n",##__VA_ARGS__) + +#if 0 +#define _enter(FMT,...) kenter(FMT,##__VA_ARGS__) +#define _leave(FMT,...) kleave(FMT,##__VA_ARGS__) +#define _debug(FMT,...) kdebug(FMT,##__VA_ARGS__) +#define _proto(FMT,...) kproto(FMT,##__VA_ARGS__) +#define _net(FMT,...) knet(FMT,##__VA_ARGS__) +#else +#define _enter(FMT,...) do { if (rxrpc_ktrace) kenter(FMT,##__VA_ARGS__); } while(0) +#define _leave(FMT,...) do { if (rxrpc_ktrace) kleave(FMT,##__VA_ARGS__); } while(0) +#define _debug(FMT,...) do { if (rxrpc_kdebug) kdebug(FMT,##__VA_ARGS__); } while(0) +#define _proto(FMT,...) do { if (rxrpc_kproto) kproto(FMT,##__VA_ARGS__); } while(0) +#define _net(FMT,...) do { if (rxrpc_knet) knet (FMT,##__VA_ARGS__); } while(0) +#endif + +static inline void rxrpc_discard_my_signals(void) +{ + while (signal_pending(current)) { + siginfo_t sinfo; + + spin_lock_irq(¤t->sig->siglock); + dequeue_signal(¤t->blocked,&sinfo); + spin_unlock_irq(¤t->sig->siglock); + } +} + +/* + * call.c + */ +extern struct list_head rxrpc_calls; +extern struct rw_semaphore rxrpc_calls_sem; + +/* + * connection.c + */ +extern struct list_head rxrpc_conns; +extern struct rw_semaphore rxrpc_conns_sem; + +extern void rxrpc_conn_do_timeout(struct rxrpc_connection *conn); +extern void rxrpc_conn_clearall(struct rxrpc_peer *peer); + +/* + * peer.c + */ +extern struct list_head rxrpc_peers; +extern struct rw_semaphore rxrpc_peers_sem; + +extern void rxrpc_peer_calculate_rtt(struct rxrpc_peer *peer, + struct rxrpc_message *msg, + struct rxrpc_message *resp); + +extern void rxrpc_peer_clearall(struct rxrpc_transport *trans); + +extern void rxrpc_peer_do_timeout(struct rxrpc_peer *peer); + + +/* + * proc.c + */ +#ifdef CONFIG_PROC_FS +extern int rxrpc_proc_init(void); +extern void rxrpc_proc_cleanup(void); +#endif + +/* + * transport.c + */ +extern struct list_head rxrpc_proc_transports; +extern struct rw_semaphore rxrpc_proc_transports_sem; + +#endif /* RXRPC_INTERNAL_H */ diff --git a/net/rxrpc/krxiod.c b/net/rxrpc/krxiod.c new file mode 100644 index 000000000000..4bbc3f6b3418 --- /dev/null +++ b/net/rxrpc/krxiod.c @@ -0,0 +1,262 @@ +/* krxiod.c: Rx I/O daemon + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +static DECLARE_WAIT_QUEUE_HEAD(rxrpc_krxiod_sleepq); +static DECLARE_COMPLETION(rxrpc_krxiod_dead); + +static atomic_t rxrpc_krxiod_qcount = ATOMIC_INIT(0); + +static LIST_HEAD(rxrpc_krxiod_transportq); +static spinlock_t rxrpc_krxiod_transportq_lock = SPIN_LOCK_UNLOCKED; + +static LIST_HEAD(rxrpc_krxiod_callq); +static spinlock_t rxrpc_krxiod_callq_lock = SPIN_LOCK_UNLOCKED; + +static volatile int rxrpc_krxiod_die; + +/*****************************************************************************/ +/* + * Rx I/O daemon + */ +static int rxrpc_krxiod(void *arg) +{ + DECLARE_WAITQUEUE(krxiod,current); + + printk("Started krxiod %d\n",current->pid); + strcpy(current->comm,"krxiod"); + + daemonize(); + + /* only certain signals are of interest */ + spin_lock_irq(¤t->sig->siglock); + siginitsetinv(¤t->blocked,0); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,3) + recalc_sigpending(); +#else + recalc_sigpending(current); +#endif + spin_unlock_irq(¤t->sig->siglock); + + /* loop around waiting for work to do */ + do { + /* wait for work or to be told to exit */ + _debug("### Begin Wait"); + if (!atomic_read(&rxrpc_krxiod_qcount)) { + set_current_state(TASK_INTERRUPTIBLE); + + add_wait_queue(&rxrpc_krxiod_sleepq,&krxiod); + + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (atomic_read(&rxrpc_krxiod_qcount) || + rxrpc_krxiod_die || + signal_pending(current)) + break; + + schedule(); + } + + remove_wait_queue(&rxrpc_krxiod_sleepq,&krxiod); + set_current_state(TASK_RUNNING); + } + _debug("### End Wait"); + + /* do work if been given some to do */ + _debug("### Begin Work"); + + /* see if there's a transport in need of attention */ + if (!list_empty(&rxrpc_krxiod_transportq)) { + struct rxrpc_transport *trans = NULL; + + spin_lock_irq(&rxrpc_krxiod_transportq_lock); + + if (!list_empty(&rxrpc_krxiod_transportq)) { + trans = list_entry(rxrpc_krxiod_transportq.next, + struct rxrpc_transport,krxiodq_link); + list_del_init(&trans->krxiodq_link); + atomic_dec(&rxrpc_krxiod_qcount); + + /* make sure it hasn't gone away and doesn't go away */ + if (atomic_read(&trans->usage)>0) + rxrpc_get_transport(trans); + else + trans = NULL; + } + + spin_unlock_irq(&rxrpc_krxiod_transportq_lock); + + if (trans) { + rxrpc_trans_receive_packet(trans); + rxrpc_put_transport(trans); + } + } + + /* see if there's a call in need of attention */ + if (!list_empty(&rxrpc_krxiod_callq)) { + struct rxrpc_call *call = NULL; + + spin_lock_irq(&rxrpc_krxiod_callq_lock); + + if (!list_empty(&rxrpc_krxiod_callq)) { + call = list_entry(rxrpc_krxiod_callq.next, + struct rxrpc_call,rcv_krxiodq_lk); + list_del_init(&call->rcv_krxiodq_lk); + atomic_dec(&rxrpc_krxiod_qcount); + + /* make sure it hasn't gone away and doesn't go away */ + if (atomic_read(&call->usage)>0) { + _debug("@@@ KRXIOD Begin Attend Call %p",call); + rxrpc_get_call(call); + } + else { + call = NULL; + } + } + + spin_unlock_irq(&rxrpc_krxiod_callq_lock); + + if (call) { + rxrpc_call_do_stuff(call); + rxrpc_put_call(call); + _debug("@@@ KRXIOD End Attend Call %p",call); + } + } + + _debug("### End Work"); + + /* discard pending signals */ + rxrpc_discard_my_signals(); + + } while (!rxrpc_krxiod_die); + + /* and that's all */ + complete_and_exit(&rxrpc_krxiod_dead,0); + +} /* end rxrpc_krxiod() */ + +/*****************************************************************************/ +/* + * start up a krxiod daemon + */ +int __init rxrpc_krxiod_init(void) +{ + return kernel_thread(rxrpc_krxiod,NULL,0); + +} /* end rxrpc_krxiod_init() */ + +/*****************************************************************************/ +/* + * kill the krxiod daemon and wait for it to complete + */ +void rxrpc_krxiod_kill(void) +{ + rxrpc_krxiod_die = 1; + wake_up_all(&rxrpc_krxiod_sleepq); + wait_for_completion(&rxrpc_krxiod_dead); + +} /* end rxrpc_krxiod_kill() */ + +/*****************************************************************************/ +/* + * queue a transport for attention by krxiod + */ +void rxrpc_krxiod_queue_transport(struct rxrpc_transport *trans) +{ + unsigned long flags; + + _enter(""); + + if (list_empty(&trans->krxiodq_link)) { + spin_lock_irqsave(&rxrpc_krxiod_transportq_lock,flags); + + if (list_empty(&trans->krxiodq_link)) { + if (atomic_read(&trans->usage)>0) { + list_add_tail(&trans->krxiodq_link,&rxrpc_krxiod_transportq); + atomic_inc(&rxrpc_krxiod_qcount); + } + } + + spin_unlock_irqrestore(&rxrpc_krxiod_transportq_lock,flags); + wake_up_all(&rxrpc_krxiod_sleepq); + } + + _leave(""); + +} /* end rxrpc_krxiod_queue_transport() */ + +/*****************************************************************************/ +/* + * dequeue a transport from krxiod's attention queue + */ +void rxrpc_krxiod_dequeue_transport(struct rxrpc_transport *trans) +{ + unsigned long flags; + + _enter(""); + + spin_lock_irqsave(&rxrpc_krxiod_transportq_lock,flags); + if (!list_empty(&trans->krxiodq_link)) { + list_del_init(&trans->krxiodq_link); + atomic_dec(&rxrpc_krxiod_qcount); + } + spin_unlock_irqrestore(&rxrpc_krxiod_transportq_lock,flags); + + _leave(""); + +} /* end rxrpc_krxiod_dequeue_transport() */ + +/*****************************************************************************/ +/* + * queue a call for attention by krxiod + */ +void rxrpc_krxiod_queue_call(struct rxrpc_call *call) +{ + unsigned long flags; + + if (list_empty(&call->rcv_krxiodq_lk)) { + spin_lock_irqsave(&rxrpc_krxiod_callq_lock,flags); + if (atomic_read(&call->usage)>0) { + list_add_tail(&call->rcv_krxiodq_lk,&rxrpc_krxiod_callq); + atomic_inc(&rxrpc_krxiod_qcount); + } + spin_unlock_irqrestore(&rxrpc_krxiod_callq_lock,flags); + } + wake_up_all(&rxrpc_krxiod_sleepq); + +} /* end rxrpc_krxiod_queue_call() */ + +/*****************************************************************************/ +/* + * dequeue a call from krxiod's attention queue + */ +void rxrpc_krxiod_dequeue_call(struct rxrpc_call *call) +{ + unsigned long flags; + + spin_lock_irqsave(&rxrpc_krxiod_callq_lock,flags); + if (!list_empty(&call->rcv_krxiodq_lk)) { + list_del_init(&call->rcv_krxiodq_lk); + atomic_dec(&rxrpc_krxiod_qcount); + } + spin_unlock_irqrestore(&rxrpc_krxiod_callq_lock,flags); + +} /* end rxrpc_krxiod_dequeue_call() */ diff --git a/net/rxrpc/krxsecd.c b/net/rxrpc/krxsecd.c new file mode 100644 index 000000000000..5b57c3f8d776 --- /dev/null +++ b/net/rxrpc/krxsecd.c @@ -0,0 +1,278 @@ +/* krxsecd.c: Rx security daemon + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + * + * This daemon deals with: + * - consulting the application as to whether inbound peers and calls should be authorised + * - generating security challenges for inbound connections + * - responding to security challenges on outbound connections + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +static DECLARE_WAIT_QUEUE_HEAD(rxrpc_krxsecd_sleepq); +static DECLARE_COMPLETION(rxrpc_krxsecd_dead); +static volatile int rxrpc_krxsecd_die; + +static atomic_t rxrpc_krxsecd_qcount; + +/* queue of unprocessed inbound messages with seqno #1 and RXRPC_CLIENT_INITIATED flag set */ +static LIST_HEAD(rxrpc_krxsecd_initmsgq); +static spinlock_t rxrpc_krxsecd_initmsgq_lock = SPIN_LOCK_UNLOCKED; + +static void rxrpc_krxsecd_process_incoming_call(struct rxrpc_message *msg); + +/*****************************************************************************/ +/* + * Rx security daemon + */ +static int rxrpc_krxsecd(void *arg) +{ + DECLARE_WAITQUEUE(krxsecd,current); + + int die; + + printk("Started krxsecd %d\n",current->pid); + strcpy(current->comm,"krxsecd"); + + daemonize(); + + /* only certain signals are of interest */ + spin_lock_irq(¤t->sig->siglock); + siginitsetinv(¤t->blocked,0); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,3) + recalc_sigpending(); +#else + recalc_sigpending(current); +#endif + spin_unlock_irq(¤t->sig->siglock); + + /* loop around waiting for work to do */ + do { + /* wait for work or to be told to exit */ + _debug("### Begin Wait"); + if (!atomic_read(&rxrpc_krxsecd_qcount)) { + set_current_state(TASK_INTERRUPTIBLE); + + add_wait_queue(&rxrpc_krxsecd_sleepq,&krxsecd); + + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (atomic_read(&rxrpc_krxsecd_qcount) || + rxrpc_krxsecd_die || + signal_pending(current)) + break; + + schedule(); + } + + remove_wait_queue(&rxrpc_krxsecd_sleepq,&krxsecd); + set_current_state(TASK_RUNNING); + } + die = rxrpc_krxsecd_die; + _debug("### End Wait"); + + /* see if there're incoming calls in need of authenticating */ + _debug("### Begin Inbound Calls"); + + if (!list_empty(&rxrpc_krxsecd_initmsgq)) { + struct rxrpc_message *msg = NULL; + + spin_lock(&rxrpc_krxsecd_initmsgq_lock); + + if (!list_empty(&rxrpc_krxsecd_initmsgq)) { + msg = list_entry(rxrpc_krxsecd_initmsgq.next, + struct rxrpc_message,link); + list_del_init(&msg->link); + atomic_dec(&rxrpc_krxsecd_qcount); + } + + spin_unlock(&rxrpc_krxsecd_initmsgq_lock); + + if (msg) { + rxrpc_krxsecd_process_incoming_call(msg); + rxrpc_put_message(msg); + } + } + + _debug("### End Inbound Calls"); + + /* discard pending signals */ + rxrpc_discard_my_signals(); + + } while (!die); + + /* and that's all */ + complete_and_exit(&rxrpc_krxsecd_dead,0); + +} /* end rxrpc_krxsecd() */ + +/*****************************************************************************/ +/* + * start up a krxsecd daemon + */ +int __init rxrpc_krxsecd_init(void) +{ + return kernel_thread(rxrpc_krxsecd,NULL,0); + +} /* end rxrpc_krxsecd_init() */ + +/*****************************************************************************/ +/* + * kill the krxsecd daemon and wait for it to complete + */ +void rxrpc_krxsecd_kill(void) +{ + rxrpc_krxsecd_die = 1; + wake_up_all(&rxrpc_krxsecd_sleepq); + wait_for_completion(&rxrpc_krxsecd_dead); + +} /* end rxrpc_krxsecd_kill() */ + +/*****************************************************************************/ +/* + * clear all pending incoming calls for the specified transport + */ +void rxrpc_krxsecd_clear_transport(struct rxrpc_transport *trans) +{ + LIST_HEAD(tmp); + + struct rxrpc_message *msg; + struct list_head *_p, *_n; + + _enter("%p",trans); + + /* move all the messages for this transport onto a temp list */ + spin_lock(&rxrpc_krxsecd_initmsgq_lock); + + list_for_each_safe(_p,_n,&rxrpc_krxsecd_initmsgq) { + msg = list_entry(_p,struct rxrpc_message,link); + if (msg->trans==trans) { + list_del(&msg->link); + list_add_tail(&msg->link,&tmp); + atomic_dec(&rxrpc_krxsecd_qcount); + } + } + + spin_unlock(&rxrpc_krxsecd_initmsgq_lock); + + /* zap all messages on the temp list */ + while (!list_empty(&tmp)) { + msg = list_entry(tmp.next,struct rxrpc_message,link); + list_del_init(&msg->link); + rxrpc_put_message(msg); + } + + _leave(""); +} /* end rxrpc_krxsecd_clear_transport() */ + +/*****************************************************************************/ +/* + * queue a message on the incoming calls list + */ +void rxrpc_krxsecd_queue_incoming_call(struct rxrpc_message *msg) +{ + _enter("%p",msg); + + /* queue for processing by krxsecd */ + spin_lock(&rxrpc_krxsecd_initmsgq_lock); + + if (!rxrpc_krxsecd_die) { + rxrpc_get_message(msg); + list_add_tail(&msg->link,&rxrpc_krxsecd_initmsgq); + atomic_inc(&rxrpc_krxsecd_qcount); + } + + spin_unlock(&rxrpc_krxsecd_initmsgq_lock); + + wake_up(&rxrpc_krxsecd_sleepq); + + _leave(""); +} /* end rxrpc_krxsecd_queue_incoming_call() */ + +/*****************************************************************************/ +/* + * process the initial message of an incoming call + */ +void rxrpc_krxsecd_process_incoming_call(struct rxrpc_message *msg) +{ + struct rxrpc_transport *trans = msg->trans; + struct rxrpc_service *srv; + struct rxrpc_call *call; + struct list_head *_p; + unsigned short sid; + int ret; + + _enter("%p{tr=%p}",msg,trans); + + ret = rxrpc_incoming_call(msg->conn,msg,&call); + if (ret<0) + goto out; + + /* find the matching service on the transport */ + sid = ntohs(msg->hdr.serviceId); + srv = NULL; + + spin_lock(&trans->lock); + list_for_each(_p,&trans->services) { + srv = list_entry(_p,struct rxrpc_service,link); + if (srv->service_id==sid && try_inc_mod_count(srv->owner)) { + /* found a match (made sure it won't vanish) */ + _debug("found service '%s'",srv->name); + call->owner = srv->owner; + break; + } + } + spin_unlock(&trans->lock); + + /* report the new connection + * - the func must inc the call's usage count to keep it + */ + ret = -ENOENT; + if (_p!=&trans->services) { + /* attempt to accept the call */ + call->conn->service = srv; + call->app_attn_func = srv->attn_func; + call->app_error_func = srv->error_func; + call->app_aemap_func = srv->aemap_func; + + ret = srv->new_call(call); + + /* send an abort if an error occurred */ + if (ret<0) { + rxrpc_call_abort(call,ret); + } + else { + /* formally receive and ACK the new packet */ + ret = rxrpc_conn_receive_call_packet(call->conn,call,msg); + } + } + + rxrpc_put_call(call); + out: + if (ret<0) + rxrpc_trans_immediate_abort(trans,msg,ret); + + _leave(" (%d)",ret); +} /* end rxrpc_krxsecd_process_incoming_call() */ diff --git a/net/rxrpc/krxtimod.c b/net/rxrpc/krxtimod.c new file mode 100644 index 000000000000..8dc986f79a6f --- /dev/null +++ b/net/rxrpc/krxtimod.c @@ -0,0 +1,210 @@ +/* krxtimod.c: RXRPC timeout daemon + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +static DECLARE_COMPLETION(krxtimod_alive); +static DECLARE_COMPLETION(krxtimod_dead); +static DECLARE_WAIT_QUEUE_HEAD(krxtimod_sleepq); +static int krxtimod_die; + +static LIST_HEAD(krxtimod_list); +static spinlock_t krxtimod_lock = SPIN_LOCK_UNLOCKED; + +static int krxtimod(void *arg); + +/*****************************************************************************/ +/* + * start the timeout daemon + */ +int rxrpc_krxtimod_start(void) +{ + int ret; + + ret = kernel_thread(krxtimod,NULL,0); + if (ret<0) + return ret; + + wait_for_completion(&krxtimod_alive); + + return ret; +} /* end rxrpc_krxtimod_start() */ + +/*****************************************************************************/ +/* + * stop the timeout daemon + */ +void rxrpc_krxtimod_kill(void) +{ + /* get rid of my daemon */ + krxtimod_die = 1; + wake_up(&krxtimod_sleepq); + wait_for_completion(&krxtimod_dead); + +} /* end rxrpc_krxtimod_kill() */ + +/*****************************************************************************/ +/* + * timeout processing daemon + */ +static int krxtimod(void *arg) +{ + DECLARE_WAITQUEUE(myself,current); + + rxrpc_timer_t *timer; + + printk("Started krxtimod %d\n",current->pid); + strcpy(current->comm,"krxtimod"); + + daemonize(); + + complete(&krxtimod_alive); + + /* only certain signals are of interest */ + spin_lock_irq(¤t->sig->siglock); + siginitsetinv(¤t->blocked,0); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,3) + recalc_sigpending(); +#else + recalc_sigpending(current); +#endif + spin_unlock_irq(¤t->sig->siglock); + + /* loop around looking for things to attend to */ + loop: + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&krxtimod_sleepq,&myself); + + for (;;) { + unsigned long jif; + signed long timeout; + + /* deal with the server being asked to die */ + if (krxtimod_die) { + remove_wait_queue(&krxtimod_sleepq,&myself); + _leave(""); + complete_and_exit(&krxtimod_dead,0); + } + + /* discard pending signals */ + rxrpc_discard_my_signals(); + + /* work out the time to elapse before the next event */ + spin_lock(&krxtimod_lock); + if (list_empty(&krxtimod_list)) { + timeout = MAX_SCHEDULE_TIMEOUT; + } + else { + timer = list_entry(krxtimod_list.next,rxrpc_timer_t,link); + timeout = timer->timo_jif; + jif = jiffies; + + if (time_before_eq(timeout,jif)) + goto immediate; + + else { + timeout = (long)timeout - (long)jiffies; + } + } + spin_unlock(&krxtimod_lock); + + schedule_timeout(timeout); + + set_current_state(TASK_INTERRUPTIBLE); + } + + /* the thing on the front of the queue needs processing + * - we come here with the lock held and timer pointing to the expired entry + */ + immediate: + remove_wait_queue(&krxtimod_sleepq,&myself); + set_current_state(TASK_RUNNING); + + _debug("@@@ Begin Timeout of %p",timer); + + /* dequeue the timer */ + list_del_init(&timer->link); + spin_unlock(&krxtimod_lock); + + /* call the timeout function */ + timer->ops->timed_out(timer); + + _debug("@@@ End Timeout"); + goto loop; + +} /* end krxtimod() */ + +/*****************************************************************************/ +/* + * (re-)queue a timer + */ +void rxrpc_krxtimod_add_timer(rxrpc_timer_t *timer, unsigned long timeout) +{ + struct list_head *_p; + rxrpc_timer_t *ptimer; + + _enter("%p,%lu",timer,timeout); + + spin_lock(&krxtimod_lock); + + list_del(&timer->link); + + /* the timer was deferred or reset - put it back in the queue at the right place */ + timer->timo_jif = jiffies + timeout; + + list_for_each(_p,&krxtimod_list) { + ptimer = list_entry(_p,rxrpc_timer_t,link); + if (time_before(timer->timo_jif,ptimer->timo_jif)) + break; + } + + list_add_tail(&timer->link,_p); /* insert before stopping point */ + + spin_unlock(&krxtimod_lock); + + wake_up(&krxtimod_sleepq); + + _leave(""); +} /* end rxrpc_krxtimod_queue_vlocation() */ + +/*****************************************************************************/ +/* + * dequeue a timer + * - returns 0 if the timer was deleted or -ENOENT if it wasn't queued + */ +int rxrpc_krxtimod_del_timer(rxrpc_timer_t *timer) +{ + int ret = 0; + + _enter("%p",timer); + + spin_lock(&krxtimod_lock); + + if (list_empty(&timer->link)) + ret = -ENOENT; + else + list_del_init(&timer->link); + + spin_unlock(&krxtimod_lock); + + wake_up(&krxtimod_sleepq); + + _leave(" = %d",ret); + return ret; +} /* end rxrpc_krxtimod_del_timer() */ diff --git a/net/rxrpc/main.c b/net/rxrpc/main.c new file mode 100644 index 000000000000..04bb3faa42d1 --- /dev/null +++ b/net/rxrpc/main.c @@ -0,0 +1,127 @@ +/* main.c: Rx RPC interface + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +static int rxrpc_initialise(void); +static void rxrpc_cleanup(void); + +module_init(rxrpc_initialise); +module_exit(rxrpc_cleanup); + +MODULE_DESCRIPTION("Rx RPC implementation"); +MODULE_AUTHOR("Red Hat, Inc."); +MODULE_LICENSE("GPL"); + +u32 rxrpc_epoch; + +/*****************************************************************************/ +/* + * initialise the Rx module + */ +static int rxrpc_initialise(void) +{ + int ret; + + /* my epoch value */ + rxrpc_epoch = htonl(xtime.tv_sec); + + /* register the /proc interface */ +#ifdef CONFIG_PROC_FS + ret = rxrpc_proc_init(); + if (ret<0) + return ret; +#endif + + /* register the sysctl files */ +#ifdef CONFIG_SYSCTL + ret = rxrpc_sysctl_init(); + if (ret<0) + goto error_proc; +#endif + + /* start the krxtimod daemon */ + ret = rxrpc_krxtimod_start(); + if (ret<0) + goto error_sysctl; + + /* start the krxiod daemon */ + ret = rxrpc_krxiod_init(); + if (ret<0) + goto error_krxtimod; + + /* start the krxsecd daemon */ + ret = rxrpc_krxsecd_init(); + if (ret<0) + goto error_krxiod; + + kdebug("\n\n"); + + return 0; + + error_krxiod: + rxrpc_krxiod_kill(); + error_krxtimod: + rxrpc_krxtimod_kill(); + error_sysctl: +#ifdef CONFIG_SYSCTL + rxrpc_sysctl_cleanup(); +#endif + error_proc: +#ifdef CONFIG_PROC_FS + rxrpc_proc_cleanup(); +#endif + return ret; +} /* end rxrpc_initialise() */ + +/*****************************************************************************/ +/* + * clean up the Rx module + */ +static void rxrpc_cleanup(void) +{ + kenter(""); + + __RXACCT(printk("Outstanding Messages : %d\n",atomic_read(&rxrpc_message_count))); + __RXACCT(printk("Outstanding Calls : %d\n",atomic_read(&rxrpc_call_count))); + __RXACCT(printk("Outstanding Connections: %d\n",atomic_read(&rxrpc_connection_count))); + __RXACCT(printk("Outstanding Peers : %d\n",atomic_read(&rxrpc_peer_count))); + __RXACCT(printk("Outstanding Transports : %d\n",atomic_read(&rxrpc_transport_count))); + + rxrpc_krxsecd_kill(); + rxrpc_krxiod_kill(); + rxrpc_krxtimod_kill(); +#ifdef CONFIG_SYSCTL + rxrpc_sysctl_cleanup(); +#endif +#ifdef CONFIG_PROC_FS + rxrpc_proc_cleanup(); +#endif + + __RXACCT(printk("Outstanding Messages : %d\n",atomic_read(&rxrpc_message_count))); + __RXACCT(printk("Outstanding Calls : %d\n",atomic_read(&rxrpc_call_count))); + __RXACCT(printk("Outstanding Connections: %d\n",atomic_read(&rxrpc_connection_count))); + __RXACCT(printk("Outstanding Peers : %d\n",atomic_read(&rxrpc_peer_count))); + __RXACCT(printk("Outstanding Transports : %d\n",atomic_read(&rxrpc_transport_count))); + + kleave(); +} /* end rxrpc_cleanup() */ diff --git a/net/rxrpc/peer.c b/net/rxrpc/peer.c new file mode 100644 index 000000000000..cdd90014b6af --- /dev/null +++ b/net/rxrpc/peer.c @@ -0,0 +1,380 @@ +/* peer.c: Rx RPC peer management + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +__RXACCT_DECL(atomic_t rxrpc_peer_count); +LIST_HEAD(rxrpc_peers); +DECLARE_RWSEM(rxrpc_peers_sem); + +static void __rxrpc_peer_timeout(rxrpc_timer_t *timer) +{ + struct rxrpc_peer *peer = list_entry(timer,struct rxrpc_peer,timeout); + + _debug("Rx PEER TIMEOUT [%p{u=%d}]",peer,atomic_read(&peer->usage)); + + rxrpc_peer_do_timeout(peer); +} + +static const struct rxrpc_timer_ops rxrpc_peer_timer_ops = { + .timed_out = __rxrpc_peer_timeout, +}; + +/*****************************************************************************/ +/* + * create a peer record + */ +static int __rxrpc_create_peer(struct rxrpc_transport *trans, u32 addr, struct rxrpc_peer **_peer) +{ + struct rxrpc_peer *peer; + + _enter("%p,%08x",trans,ntohl(addr)); + + /* allocate and initialise a peer record */ + peer = kmalloc(sizeof(struct rxrpc_peer),GFP_KERNEL); + if (!peer) { + _leave(" = -ENOMEM"); + return -ENOMEM; + } + + memset(peer,0,sizeof(struct rxrpc_peer)); + atomic_set(&peer->usage,1); + + INIT_LIST_HEAD(&peer->link); + INIT_LIST_HEAD(&peer->proc_link); + INIT_LIST_HEAD(&peer->conn_active); + INIT_LIST_HEAD(&peer->conn_graveyard); + spin_lock_init(&peer->conn_gylock); + init_waitqueue_head(&peer->conn_gy_waitq); + rwlock_init(&peer->conn_lock); + atomic_set(&peer->conn_count,0); + spin_lock_init(&peer->lock); + rxrpc_timer_init(&peer->timeout,&rxrpc_peer_timer_ops); + + peer->addr.s_addr = addr; + + peer->trans = trans; + peer->ops = trans->peer_ops; + + __RXACCT(atomic_inc(&rxrpc_peer_count)); + *_peer = peer; + _leave(" = 0 (%p)",peer); + + return 0; +} /* end __rxrpc_create_peer() */ + +/*****************************************************************************/ +/* + * find a peer record on the specified transport + * - returns (if successful) with peer record usage incremented + * - resurrects it from the graveyard if found there + */ +int rxrpc_peer_lookup(struct rxrpc_transport *trans, u32 addr, struct rxrpc_peer **_peer) +{ + struct rxrpc_peer *peer, *candidate = NULL; + struct list_head *_p; + int ret; + + _enter("%p{%hu},%08x",trans,trans->port,ntohl(addr)); + + /* [common case] search the transport's active list first */ + read_lock(&trans->peer_lock); + list_for_each(_p,&trans->peer_active) { + peer = list_entry(_p,struct rxrpc_peer,link); + if (peer->addr.s_addr==addr) + goto found_active; + } + read_unlock(&trans->peer_lock); + + /* [uncommon case] not active - create a candidate for a new record */ + ret = __rxrpc_create_peer(trans,addr,&candidate); + if (ret<0) { + _leave(" = %d",ret); + return ret; + } + + /* search the active list again, just in case it appeared whilst we were busy */ + write_lock(&trans->peer_lock); + list_for_each(_p,&trans->peer_active) { + peer = list_entry(_p,struct rxrpc_peer,link); + if (peer->addr.s_addr==addr) + goto found_active_second_chance; + } + + /* search the transport's graveyard list */ + spin_lock(&trans->peer_gylock); + list_for_each(_p,&trans->peer_graveyard) { + peer = list_entry(_p,struct rxrpc_peer,link); + if (peer->addr.s_addr==addr) + goto found_in_graveyard; + } + spin_unlock(&trans->peer_gylock); + + /* we can now add the new candidate to the list + * - tell the application layer that this peer has been added + */ + rxrpc_get_transport(trans); + peer = candidate; + candidate = NULL; + + if (peer->ops && peer->ops->adding) { + ret = peer->ops->adding(peer); + if (ret<0) { + write_unlock(&trans->peer_lock); + __RXACCT(atomic_dec(&rxrpc_peer_count)); + kfree(peer); + rxrpc_put_transport(trans); + _leave(" = %d",ret); + return ret; + } + } + + atomic_inc(&trans->peer_count); + + make_active: + list_add_tail(&peer->link,&trans->peer_active); + + success_uwfree: + write_unlock(&trans->peer_lock); + + if (candidate) { + __RXACCT(atomic_dec(&rxrpc_peer_count)); + kfree(candidate); + } + + if (list_empty(&peer->proc_link)) { + down_write(&rxrpc_peers_sem); + list_add_tail(&peer->proc_link,&rxrpc_peers); + up_write(&rxrpc_peers_sem); + } + + success: + *_peer = peer; + + _leave(" = 0 (%p{u=%d cc=%d})", + peer,atomic_read(&peer->usage),atomic_read(&peer->conn_count)); + return 0; + + /* handle the peer being found in the active list straight off */ + found_active: + rxrpc_get_peer(peer); + read_unlock(&trans->peer_lock); + goto success; + + /* handle resurrecting a peer from the graveyard */ + found_in_graveyard: + rxrpc_get_peer(peer); + rxrpc_get_transport(peer->trans); + rxrpc_krxtimod_del_timer(&peer->timeout); + list_del_init(&peer->link); + spin_unlock(&trans->peer_gylock); + goto make_active; + + /* handle finding the peer on the second time through the active list */ + found_active_second_chance: + rxrpc_get_peer(peer); + goto success_uwfree; + +} /* end rxrpc_peer_lookup() */ + +/*****************************************************************************/ +/* + * finish with a peer record + * - it gets sent to the graveyard from where it can be resurrected or timed out + */ +void rxrpc_put_peer(struct rxrpc_peer *peer) +{ + struct rxrpc_transport *trans = peer->trans; + + _enter("%p{cc=%d a=%08x}",peer,atomic_read(&peer->conn_count),ntohl(peer->addr.s_addr)); + + /* sanity check */ + if (atomic_read(&peer->usage)<=0) + BUG(); + + write_lock(&trans->peer_lock); + spin_lock(&trans->peer_gylock); + if (likely(!atomic_dec_and_test(&peer->usage))) { + spin_unlock(&trans->peer_gylock); + write_unlock(&trans->peer_lock); + _leave(""); + return; + } + + /* move to graveyard queue */ + list_del(&peer->link); + write_unlock(&trans->peer_lock); + + list_add_tail(&peer->link,&trans->peer_graveyard); + + if (!list_empty(&peer->conn_active)) BUG(); + + /* discard in 600 secs */ + rxrpc_krxtimod_add_timer(&peer->timeout,100*HZ); + + spin_unlock(&trans->peer_gylock); + + rxrpc_put_transport(trans); + + _leave(" [killed]"); +} /* end rxrpc_put_peer() */ + +/*****************************************************************************/ +/* + * handle a peer timing out in the graveyard + * - called from krxtimod + */ +void rxrpc_peer_do_timeout(struct rxrpc_peer *peer) +{ + struct rxrpc_transport *trans = peer->trans; + + _enter("%p{u=%d cc=%d a=%08x}", + peer,atomic_read(&peer->usage),atomic_read(&peer->conn_count), + ntohl(peer->addr.s_addr)); + + if (atomic_read(&peer->usage)<0) + BUG(); + + /* remove from graveyard if still dead */ + spin_lock(&trans->peer_gylock); + if (atomic_read(&peer->usage)==0) + list_del_init(&peer->link); + else + peer = NULL; + spin_unlock(&trans->peer_gylock); + + if (!peer) { + _leave(""); + return; /* resurrected */ + } + + /* clear all connections on this peer */ + rxrpc_conn_clearall(peer); + + if (!list_empty(&peer->conn_active)) BUG(); + if (!list_empty(&peer->conn_graveyard)) BUG(); + + /* inform the application layer */ + if (peer->ops && peer->ops->discarding) + peer->ops->discarding(peer); + + if (!list_empty(&peer->proc_link)) { + down_write(&rxrpc_peers_sem); + list_del(&peer->proc_link); + up_write(&rxrpc_peers_sem); + } + + __RXACCT(atomic_dec(&rxrpc_peer_count)); + kfree(peer); + + /* if the graveyard is now empty, wake up anyone waiting for that */ + if (atomic_dec_and_test(&trans->peer_count)) + wake_up(&trans->peer_gy_waitq); + + _leave(" [destroyed]"); +} /* end rxrpc_peer_do_timeout() */ + +/*****************************************************************************/ +/* + * clear all peer records from a transport endpoint + */ +void rxrpc_peer_clearall(struct rxrpc_transport *trans) +{ + DECLARE_WAITQUEUE(myself,current); + + struct rxrpc_peer *peer; + int err; + + _enter("%p",trans); + + /* there shouldn't be any active peers remaining */ + if (!list_empty(&trans->peer_active)) + BUG(); + + /* manually timeout all peers in the graveyard */ + spin_lock(&trans->peer_gylock); + while (!list_empty(&trans->peer_graveyard)) { + peer = list_entry(trans->peer_graveyard.next,struct rxrpc_peer,link); + _debug("Clearing peer %p\n",peer); + err = rxrpc_krxtimod_del_timer(&peer->timeout); + spin_unlock(&trans->peer_gylock); + + if (err==0) + rxrpc_peer_do_timeout(peer); + + spin_lock(&trans->peer_gylock); + } + spin_unlock(&trans->peer_gylock); + + /* wait for the the peer graveyard to be completely cleared */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&trans->peer_gy_waitq,&myself); + + while (atomic_read(&trans->peer_count)!=0) { + schedule(); + set_current_state(TASK_UNINTERRUPTIBLE); + } + + remove_wait_queue(&trans->peer_gy_waitq,&myself); + set_current_state(TASK_RUNNING); + + _leave(""); + +} /* end rxrpc_peer_clearall() */ + +/*****************************************************************************/ +/* + * calculate and cache the Round-Trip-Time for a message and its response + */ +void rxrpc_peer_calculate_rtt(struct rxrpc_peer *peer, + struct rxrpc_message *msg, + struct rxrpc_message *resp) +{ + unsigned long long rtt; + int loop; + + _enter("%p,%p,%p",peer,msg,resp); + + /* calculate the latest RTT */ + rtt = resp->stamp.tv_sec - msg->stamp.tv_sec; + rtt *= 1000000UL; + rtt += resp->stamp.tv_usec - msg->stamp.tv_usec; + + /* add to cache */ + peer->rtt_cache[peer->rtt_point] = rtt; + peer->rtt_point++; + peer->rtt_point %= RXRPC_RTT_CACHE_SIZE; + + if (peer->rtt_usagertt_usage++; + + /* recalculate RTT */ + for (loop=peer->rtt_usage-1; loop>=0; loop--) + rtt += peer->rtt_cache[loop]; + + peer->rtt = do_div(rtt,peer->rtt_usage); + + _leave(" RTT=%lu.%lums",peer->rtt/1000,peer->rtt%1000); + +} /* end rxrpc_peer_calculate_rtt() */ diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c new file mode 100644 index 000000000000..aeb9d9e8ddae --- /dev/null +++ b/net/rxrpc/proc.c @@ -0,0 +1,612 @@ +/* proc.c: /proc interface for RxRPC + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +static inline struct proc_dir_entry *PDE(const struct inode *inode) +{ + return (struct proc_dir_entry *)inode->u.generic_ip; +} +#endif + +static struct proc_dir_entry *proc_rxrpc; + +static int rxrpc_proc_transports_open(struct inode *inode, struct file *file); +static void *rxrpc_proc_transports_start(struct seq_file *p, loff_t *pos); +static void *rxrpc_proc_transports_next(struct seq_file *p, void *v, loff_t *pos); +static void rxrpc_proc_transports_stop(struct seq_file *p, void *v); +static int rxrpc_proc_transports_show(struct seq_file *m, void *v); + +static struct seq_operations rxrpc_proc_transports_ops = { + .start = rxrpc_proc_transports_start, + .next = rxrpc_proc_transports_next, + .stop = rxrpc_proc_transports_stop, + .show = rxrpc_proc_transports_show, +}; + +static struct file_operations rxrpc_proc_transports_fops = { + .open = rxrpc_proc_transports_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int rxrpc_proc_peers_open(struct inode *inode, struct file *file); +static void *rxrpc_proc_peers_start(struct seq_file *p, loff_t *pos); +static void *rxrpc_proc_peers_next(struct seq_file *p, void *v, loff_t *pos); +static void rxrpc_proc_peers_stop(struct seq_file *p, void *v); +static int rxrpc_proc_peers_show(struct seq_file *m, void *v); + +static struct seq_operations rxrpc_proc_peers_ops = { + .start = rxrpc_proc_peers_start, + .next = rxrpc_proc_peers_next, + .stop = rxrpc_proc_peers_stop, + .show = rxrpc_proc_peers_show, +}; + +static struct file_operations rxrpc_proc_peers_fops = { + .open = rxrpc_proc_peers_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int rxrpc_proc_conns_open(struct inode *inode, struct file *file); +static void *rxrpc_proc_conns_start(struct seq_file *p, loff_t *pos); +static void *rxrpc_proc_conns_next(struct seq_file *p, void *v, loff_t *pos); +static void rxrpc_proc_conns_stop(struct seq_file *p, void *v); +static int rxrpc_proc_conns_show(struct seq_file *m, void *v); + +static struct seq_operations rxrpc_proc_conns_ops = { + .start = rxrpc_proc_conns_start, + .next = rxrpc_proc_conns_next, + .stop = rxrpc_proc_conns_stop, + .show = rxrpc_proc_conns_show, +}; + +static struct file_operations rxrpc_proc_conns_fops = { + .open = rxrpc_proc_conns_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int rxrpc_proc_calls_open(struct inode *inode, struct file *file); +static void *rxrpc_proc_calls_start(struct seq_file *p, loff_t *pos); +static void *rxrpc_proc_calls_next(struct seq_file *p, void *v, loff_t *pos); +static void rxrpc_proc_calls_stop(struct seq_file *p, void *v); +static int rxrpc_proc_calls_show(struct seq_file *m, void *v); + +static struct seq_operations rxrpc_proc_calls_ops = { + .start = rxrpc_proc_calls_start, + .next = rxrpc_proc_calls_next, + .stop = rxrpc_proc_calls_stop, + .show = rxrpc_proc_calls_show, +}; + +static struct file_operations rxrpc_proc_calls_fops = { + .open = rxrpc_proc_calls_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static const char *rxrpc_call_states7[] = { + "complet", + "error ", + "rcv_op ", + "rcv_arg", + "got_arg", + "snd_rpl", + "fin_ack", + "snd_arg", + "rcv_rpl", + "got_rpl" +}; + +static const char *rxrpc_call_error_states7[] = { + "no_err ", + "loc_abt", + "rmt_abt", + "loc_err", + "rmt_err" +}; + +/*****************************************************************************/ +/* + * initialise the /proc/net/rxrpc/ directory + */ +int rxrpc_proc_init(void) +{ + struct proc_dir_entry *p; + + proc_rxrpc = proc_mkdir("rxrpc",proc_net); + if (!proc_rxrpc) + goto error; + proc_rxrpc->owner = THIS_MODULE; + + p = create_proc_entry("calls",0,proc_rxrpc); + if (!p) + goto error_proc; + p->proc_fops = &rxrpc_proc_calls_fops; + p->owner = THIS_MODULE; + + p = create_proc_entry("connections",0,proc_rxrpc); + if (!p) + goto error_calls; + p->proc_fops = &rxrpc_proc_conns_fops; + p->owner = THIS_MODULE; + + p = create_proc_entry("peers",0,proc_rxrpc); + if (!p) + goto error_calls; + p->proc_fops = &rxrpc_proc_peers_fops; + p->owner = THIS_MODULE; + + p = create_proc_entry("transports",0,proc_rxrpc); + if (!p) + goto error_conns; + p->proc_fops = &rxrpc_proc_transports_fops; + p->owner = THIS_MODULE; + + return 0; + + error_conns: + remove_proc_entry("conns",proc_rxrpc); + error_calls: + remove_proc_entry("calls",proc_rxrpc); + error_proc: + remove_proc_entry("rxrpc",proc_net); + error: + return -ENOMEM; +} /* end rxrpc_proc_init() */ + +/*****************************************************************************/ +/* + * clean up the /proc/net/rxrpc/ directory + */ +void rxrpc_proc_cleanup(void) +{ + remove_proc_entry("transports",proc_rxrpc); + remove_proc_entry("peers",proc_rxrpc); + remove_proc_entry("connections",proc_rxrpc); + remove_proc_entry("calls",proc_rxrpc); + + remove_proc_entry("rxrpc",proc_net); + +} /* end rxrpc_proc_cleanup() */ + +/*****************************************************************************/ +/* + * open "/proc/net/rxrpc/transports" which provides a summary of extant transports + */ +static int rxrpc_proc_transports_open(struct inode *inode, struct file *file) +{ + struct seq_file *m; + int ret; + + ret = seq_open(file,&rxrpc_proc_transports_ops); + if (ret<0) + return ret; + + m = file->private_data; + m->private = PDE(inode)->data; + + return 0; +} /* end rxrpc_proc_transports_open() */ + +/*****************************************************************************/ +/* + * set up the iterator to start reading from the transports list and return the first item + */ +static void *rxrpc_proc_transports_start(struct seq_file *m, loff_t *_pos) +{ + struct list_head *_p; + loff_t pos = *_pos; + + /* lock the list against modification */ + down_read(&rxrpc_proc_transports_sem); + + /* allow for the header line */ + if (!pos) + return (void *)1; + pos--; + + /* find the n'th element in the list */ + list_for_each(_p,&rxrpc_proc_transports) + if (!pos--) + break; + + return _p!=&rxrpc_proc_transports ? _p : NULL; +} /* end rxrpc_proc_transports_start() */ + +/*****************************************************************************/ +/* + * move to next call in transports list + */ +static void *rxrpc_proc_transports_next(struct seq_file *p, void *v, loff_t *pos) +{ + struct list_head *_p; + + (*pos)++; + + _p = v; + _p = v==(void*)1 ? rxrpc_proc_transports.next : _p->next; + + return _p!=&rxrpc_proc_transports ? _p : NULL; +} /* end rxrpc_proc_transports_next() */ + +/*****************************************************************************/ +/* + * clean up after reading from the transports list + */ +static void rxrpc_proc_transports_stop(struct seq_file *p, void *v) +{ + up_read(&rxrpc_proc_transports_sem); + +} /* end rxrpc_proc_transports_stop() */ + +/*****************************************************************************/ +/* + * display a header line followed by a load of call lines + */ +static int rxrpc_proc_transports_show(struct seq_file *m, void *v) +{ + struct rxrpc_transport *trans = list_entry(v,struct rxrpc_transport,proc_link); + + /* display header on line 1 */ + if (v == (void *)1) { + seq_puts(m, "LOCAL USE\n"); + return 0; + } + + /* display one transport per line on subsequent lines */ + seq_printf(m,"%5hu %3d\n", + trans->port, + atomic_read(&trans->usage) + ); + + return 0; +} /* end rxrpc_proc_transports_show() */ + +/*****************************************************************************/ +/* + * open "/proc/net/rxrpc/peers" which provides a summary of extant peers + */ +static int rxrpc_proc_peers_open(struct inode *inode, struct file *file) +{ + struct seq_file *m; + int ret; + + ret = seq_open(file,&rxrpc_proc_peers_ops); + if (ret<0) + return ret; + + m = file->private_data; + m->private = PDE(inode)->data; + + return 0; +} /* end rxrpc_proc_peers_open() */ + +/*****************************************************************************/ +/* + * set up the iterator to start reading from the peers list and return the first item + */ +static void *rxrpc_proc_peers_start(struct seq_file *m, loff_t *_pos) +{ + struct list_head *_p; + loff_t pos = *_pos; + + /* lock the list against modification */ + down_read(&rxrpc_peers_sem); + + /* allow for the header line */ + if (!pos) + return (void *)1; + pos--; + + /* find the n'th element in the list */ + list_for_each(_p,&rxrpc_peers) + if (!pos--) + break; + + return _p!=&rxrpc_peers ? _p : NULL; +} /* end rxrpc_proc_peers_start() */ + +/*****************************************************************************/ +/* + * move to next conn in peers list + */ +static void *rxrpc_proc_peers_next(struct seq_file *p, void *v, loff_t *pos) +{ + struct list_head *_p; + + (*pos)++; + + _p = v; + _p = v==(void*)1 ? rxrpc_peers.next : _p->next; + + return _p!=&rxrpc_peers ? _p : NULL; +} /* end rxrpc_proc_peers_next() */ + +/*****************************************************************************/ +/* + * clean up after reading from the peers list + */ +static void rxrpc_proc_peers_stop(struct seq_file *p, void *v) +{ + up_read(&rxrpc_peers_sem); + +} /* end rxrpc_proc_peers_stop() */ + +/*****************************************************************************/ +/* + * display a header line followed by a load of conn lines + */ +static int rxrpc_proc_peers_show(struct seq_file *m, void *v) +{ + struct rxrpc_peer *peer = list_entry(v,struct rxrpc_peer,proc_link); + signed long timeout; + + /* display header on line 1 */ + if (v == (void *)1) { + seq_puts(m,"LOCAL REMOTE USAGE CONNS TIMEOUT MTU RTT(uS)\n"); + return 0; + } + + /* display one peer per line on subsequent lines */ + timeout = 0; + if (!list_empty(&peer->timeout.link)) + timeout = (signed long)peer->timeout.timo_jif - (signed long)jiffies; + + seq_printf(m,"%5hu %08x %5d %5d %8ld %5u %7lu\n", + peer->trans->port, + ntohl(peer->addr.s_addr), + atomic_read(&peer->usage), + atomic_read(&peer->conn_count), + timeout, + peer->if_mtu, + peer->rtt + ); + + return 0; +} /* end rxrpc_proc_peers_show() */ + +/*****************************************************************************/ +/* + * open "/proc/net/rxrpc/connections" which provides a summary of extant connections + */ +static int rxrpc_proc_conns_open(struct inode *inode, struct file *file) +{ + struct seq_file *m; + int ret; + + ret = seq_open(file,&rxrpc_proc_conns_ops); + if (ret<0) + return ret; + + m = file->private_data; + m->private = PDE(inode)->data; + + return 0; +} /* end rxrpc_proc_conns_open() */ + +/*****************************************************************************/ +/* + * set up the iterator to start reading from the conns list and return the first item + */ +static void *rxrpc_proc_conns_start(struct seq_file *m, loff_t *_pos) +{ + struct list_head *_p; + loff_t pos = *_pos; + + /* lock the list against modification */ + down_read(&rxrpc_conns_sem); + + /* allow for the header line */ + if (!pos) + return (void *)1; + pos--; + + /* find the n'th element in the list */ + list_for_each(_p,&rxrpc_conns) + if (!pos--) + break; + + return _p!=&rxrpc_conns ? _p : NULL; +} /* end rxrpc_proc_conns_start() */ + +/*****************************************************************************/ +/* + * move to next conn in conns list + */ +static void *rxrpc_proc_conns_next(struct seq_file *p, void *v, loff_t *pos) +{ + struct list_head *_p; + + (*pos)++; + + _p = v; + _p = v==(void*)1 ? rxrpc_conns.next : _p->next; + + return _p!=&rxrpc_conns ? _p : NULL; +} /* end rxrpc_proc_conns_next() */ + +/*****************************************************************************/ +/* + * clean up after reading from the conns list + */ +static void rxrpc_proc_conns_stop(struct seq_file *p, void *v) +{ + up_read(&rxrpc_conns_sem); + +} /* end rxrpc_proc_conns_stop() */ + +/*****************************************************************************/ +/* + * display a header line followed by a load of conn lines + */ +static int rxrpc_proc_conns_show(struct seq_file *m, void *v) +{ + struct rxrpc_connection *conn = list_entry(v,struct rxrpc_connection,proc_link); + signed long timeout; + + /* display header on line 1 */ + if (v == (void *)1) { + seq_puts(m, + "LOCAL REMOTE RPORT SRVC CONN END SERIALNO CALLNO MTU TIMEOUT" + "\n"); + return 0; + } + + /* display one conn per line on subsequent lines */ + timeout = 0; + if (!list_empty(&conn->timeout.link)) + timeout = (signed long)conn->timeout.timo_jif - (signed long)jiffies; + + seq_printf(m,"%5hu %08x %5hu %04hx %08x %-3.3s %08x %08x %5u %8ld\n", + conn->trans->port, + ntohl(conn->addr.sin_addr.s_addr), + ntohs(conn->addr.sin_port), + ntohs(conn->service_id), + ntohl(conn->conn_id), + conn->out_clientflag ? "CLT" : "SRV", + conn->serial_counter, + conn->call_counter, + conn->mtu_size, + timeout + ); + + return 0; +} /* end rxrpc_proc_conns_show() */ + +/*****************************************************************************/ +/* + * open "/proc/net/rxrpc/calls" which provides a summary of extant calls + */ +static int rxrpc_proc_calls_open(struct inode *inode, struct file *file) +{ + struct seq_file *m; + int ret; + + ret = seq_open(file,&rxrpc_proc_calls_ops); + if (ret<0) + return ret; + + m = file->private_data; + m->private = PDE(inode)->data; + + return 0; +} /* end rxrpc_proc_calls_open() */ + +/*****************************************************************************/ +/* + * set up the iterator to start reading from the calls list and return the first item + */ +static void *rxrpc_proc_calls_start(struct seq_file *m, loff_t *_pos) +{ + struct list_head *_p; + loff_t pos = *_pos; + + /* lock the list against modification */ + down_read(&rxrpc_calls_sem); + + /* allow for the header line */ + if (!pos) + return (void *)1; + pos--; + + /* find the n'th element in the list */ + list_for_each(_p,&rxrpc_calls) + if (!pos--) + break; + + return _p!=&rxrpc_calls ? _p : NULL; +} /* end rxrpc_proc_calls_start() */ + +/*****************************************************************************/ +/* + * move to next call in calls list + */ +static void *rxrpc_proc_calls_next(struct seq_file *p, void *v, loff_t *pos) +{ + struct list_head *_p; + + (*pos)++; + + _p = v; + _p = v==(void*)1 ? rxrpc_calls.next : _p->next; + + return _p!=&rxrpc_calls ? _p : NULL; +} /* end rxrpc_proc_calls_next() */ + +/*****************************************************************************/ +/* + * clean up after reading from the calls list + */ +static void rxrpc_proc_calls_stop(struct seq_file *p, void *v) +{ + up_read(&rxrpc_calls_sem); + +} /* end rxrpc_proc_calls_stop() */ + +/*****************************************************************************/ +/* + * display a header line followed by a load of call lines + */ +static int rxrpc_proc_calls_show(struct seq_file *m, void *v) +{ + struct rxrpc_call *call = list_entry(v,struct rxrpc_call,call_link); + + /* display header on line 1 */ + if (v == (void *)1) { + seq_puts(m, + "LOCAL REMOT SRVC CONN CALL DIR USE " + " L STATE OPCODE ABORT ERRNO\n" + ); + return 0; + } + + /* display one call per line on subsequent lines */ + seq_printf(m, + "%5hu %5hu %04hx %08x %08x %s %3u%c" + " %c %-7.7s %6d %08x %5d\n", + call->conn->trans->port, + ntohs(call->conn->addr.sin_port), + ntohs(call->conn->service_id), + ntohl(call->conn->conn_id), + ntohl(call->call_id), + call->conn->service ? "SVC" : "CLT", + atomic_read(&call->usage), + waitqueue_active(&call->waitq) ? 'w' : ' ', + call->app_last_rcv ? 'Y' : '-', + (call->app_call_state!=RXRPC_CSTATE_ERROR ? + rxrpc_call_states7[call->app_call_state] : + rxrpc_call_error_states7[call->app_err_state]), + call->app_opcode, + call->app_abort_code, + call->app_errno + ); + + return 0; +} /* end rxrpc_proc_calls_show() */ diff --git a/net/rxrpc/rxrpc_syms.c b/net/rxrpc/rxrpc_syms.c new file mode 100644 index 000000000000..3b33b7e5cbd7 --- /dev/null +++ b/net/rxrpc/rxrpc_syms.c @@ -0,0 +1,51 @@ +/* rxrpc_syms.c: exported Rx RPC layer interface symbols + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + +#include +#include +#include +#include + +/* call.c */ +EXPORT_SYMBOL(rxrpc_call_rcv_timeout); +EXPORT_SYMBOL(rxrpc_call_acks_timeout); +EXPORT_SYMBOL(rxrpc_call_dfr_ack_timeout); +EXPORT_SYMBOL(rxrpc_call_max_resend); +EXPORT_SYMBOL(rxrpc_call_states); +EXPORT_SYMBOL(rxrpc_call_error_states); + +EXPORT_SYMBOL(rxrpc_create_call); +EXPORT_SYMBOL(rxrpc_incoming_call); +EXPORT_SYMBOL(rxrpc_put_call); +EXPORT_SYMBOL(rxrpc_call_abort); +EXPORT_SYMBOL(rxrpc_call_read_data); +EXPORT_SYMBOL(rxrpc_call_write_data); +EXPORT_SYMBOL(rxrpc_call_flush); + +/* connection.c */ +EXPORT_SYMBOL(rxrpc_create_connection); +EXPORT_SYMBOL(rxrpc_put_connection); + +/* sysctl.c */ +EXPORT_SYMBOL(rxrpc_ktrace); +EXPORT_SYMBOL(rxrpc_kdebug); +EXPORT_SYMBOL(rxrpc_kproto); +EXPORT_SYMBOL(rxrpc_knet); + +/* transport.c */ +EXPORT_SYMBOL(rxrpc_create_transport); +EXPORT_SYMBOL(rxrpc_clear_transport); +EXPORT_SYMBOL(rxrpc_put_transport); +EXPORT_SYMBOL(rxrpc_add_service); +EXPORT_SYMBOL(rxrpc_del_service); diff --git a/net/rxrpc/sysctl.c b/net/rxrpc/sysctl.c new file mode 100644 index 000000000000..a2d0d73b59f9 --- /dev/null +++ b/net/rxrpc/sysctl.c @@ -0,0 +1,73 @@ +/* sysctl.c: Rx RPC control + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +int rxrpc_ktrace; +int rxrpc_kdebug; +int rxrpc_kproto; +int rxrpc_knet; + +#ifdef CONFIG_SYSCTL +static struct ctl_table_header *rxrpc_sysctl = NULL; + +static ctl_table rxrpc_sysctl_table[] = { + { 1, "kdebug", &rxrpc_kdebug, sizeof(int), 0644, NULL, &proc_dointvec }, + { 2, "ktrace", &rxrpc_ktrace, sizeof(int), 0644, NULL, &proc_dointvec }, + { 3, "kproto", &rxrpc_kproto, sizeof(int), 0644, NULL, &proc_dointvec }, + { 4, "knet", &rxrpc_knet, sizeof(int), 0644, NULL, &proc_dointvec }, + { 0 } +}; + +static ctl_table rxrpc_dir_sysctl_table[] = { + { 1, "rxrpc", NULL, 0, 0555, rxrpc_sysctl_table }, + { 0 } +}; +#endif /* CONFIG_SYSCTL */ + +/*****************************************************************************/ +/* + * initialise the sysctl stuff for Rx RPC + */ +int rxrpc_sysctl_init(void) +{ +#ifdef CONFIG_SYSCTL + rxrpc_sysctl = register_sysctl_table(rxrpc_dir_sysctl_table,0); + if (!rxrpc_sysctl) + return -ENOMEM; +#endif /* CONFIG_SYSCTL */ + + return 0; +} /* end rxrpc_sysctl_init() */ + +/*****************************************************************************/ +/* + * clean up the sysctl stuff for Rx RPC + */ +void rxrpc_sysctl_cleanup(void) +{ +#ifdef CONFIG_SYSCTL + if (rxrpc_sysctl) { + unregister_sysctl_table(rxrpc_sysctl); + rxrpc_sysctl = NULL; + } +#endif /* CONFIG_SYSCTL */ + +} /* end rxrpc_sysctl_cleanup() */ diff --git a/net/rxrpc/transport.c b/net/rxrpc/transport.c new file mode 100644 index 000000000000..f1dd614c7251 --- /dev/null +++ b/net/rxrpc/transport.c @@ -0,0 +1,824 @@ +/* transport.c: Rx Transport routines + * + * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) +#include /* this should _really_ be in errqueue.h.. */ +#endif +#include +#include +#include +#include "internal.h" + +struct errormsg { + struct cmsghdr cmsg; /* control message header */ + struct sock_extended_err ee; /* extended error information */ + struct sockaddr_in icmp_src; /* ICMP packet source address */ +}; + +static spinlock_t rxrpc_transports_lock = SPIN_LOCK_UNLOCKED; +static struct list_head rxrpc_transports = LIST_HEAD_INIT(rxrpc_transports); + +__RXACCT_DECL(atomic_t rxrpc_transport_count); +LIST_HEAD(rxrpc_proc_transports); +DECLARE_RWSEM(rxrpc_proc_transports_sem); + +static void rxrpc_data_ready(struct sock *sk, int count); +static void rxrpc_error_report(struct sock *sk); +static int rxrpc_trans_receive_new_call(struct rxrpc_transport *trans, + struct list_head *msgq); +static void rxrpc_trans_receive_error_report(struct rxrpc_transport *trans); + +/*****************************************************************************/ +/* + * create a new transport endpoint using the specified UDP port + */ +int rxrpc_create_transport(unsigned short port, struct rxrpc_transport **_trans) +{ + struct rxrpc_transport *trans; + struct sockaddr_in sin; + mm_segment_t oldfs; + struct sock *sock; + int ret, opt; + + _enter("%hu",port); + + trans = kmalloc(sizeof(struct rxrpc_transport),GFP_KERNEL); + if (!trans) + return -ENOMEM; + + memset(trans,0,sizeof(struct rxrpc_transport)); + atomic_set(&trans->usage,1); + INIT_LIST_HEAD(&trans->services); + INIT_LIST_HEAD(&trans->link); + INIT_LIST_HEAD(&trans->krxiodq_link); + spin_lock_init(&trans->lock); + INIT_LIST_HEAD(&trans->peer_active); + INIT_LIST_HEAD(&trans->peer_graveyard); + spin_lock_init(&trans->peer_gylock); + init_waitqueue_head(&trans->peer_gy_waitq); + rwlock_init(&trans->peer_lock); + atomic_set(&trans->peer_count,0); + trans->port = port; + + /* create a UDP socket to be my actual transport endpoint */ + ret = sock_create(PF_INET,SOCK_DGRAM,IPPROTO_UDP,&trans->socket); + if (ret<0) + goto error; + + /* use the specified port */ + if (port) { + memset(&sin,0,sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + ret = trans->socket->ops->bind(trans->socket,(struct sockaddr *)&sin,sizeof(sin)); + if (ret<0) + goto error; + } + + opt = 1; + oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = trans->socket->ops->setsockopt(trans->socket,SOL_IP,IP_RECVERR, + (char*)&opt,sizeof(opt)); + set_fs(oldfs); + + spin_lock(&rxrpc_transports_lock); + list_add(&trans->link,&rxrpc_transports); + spin_unlock(&rxrpc_transports_lock); + + /* set the socket up */ + sock = trans->socket->sk; + sock->user_data = trans; + sock->data_ready = rxrpc_data_ready; + sock->error_report = rxrpc_error_report; + + down_write(&rxrpc_proc_transports_sem); + list_add_tail(&trans->proc_link,&rxrpc_proc_transports); + up_write(&rxrpc_proc_transports_sem); + + __RXACCT(atomic_inc(&rxrpc_transport_count)); + + *_trans = trans; + _leave(" = 0 (%p)",trans); + return 0; + + error: + rxrpc_put_transport(trans); + + _leave(" = %d",ret); + + return ret; + +} /* end rxrpc_create_transport() */ + +/*****************************************************************************/ +/* + * clear the connections on a transport endpoint + */ +void rxrpc_clear_transport(struct rxrpc_transport *trans) +{ + //struct rxrpc_connection *conn; + +} /* end rxrpc_clear_transport() */ + +/*****************************************************************************/ +/* + * destroy a transport endpoint + */ +void rxrpc_put_transport(struct rxrpc_transport *trans) +{ + _enter("%p{u=%d p=%hu}",trans,atomic_read(&trans->usage),trans->port); + + if (atomic_read(&trans->usage)<=0) + BUG(); + + /* to prevent a race, the decrement and the dequeue must be effectively atomic */ + spin_lock(&rxrpc_transports_lock); + if (likely(!atomic_dec_and_test(&trans->usage))) { + spin_unlock(&rxrpc_transports_lock); + _leave(""); + return; + } + + list_del(&trans->link); + spin_unlock(&rxrpc_transports_lock); + + /* finish cleaning up the transport */ + if (trans->socket) + trans->socket->ops->shutdown(trans->socket,2); + + rxrpc_krxsecd_clear_transport(trans); + rxrpc_krxiod_dequeue_transport(trans); + + /* discard all peer information */ + rxrpc_peer_clearall(trans); + + down_write(&rxrpc_proc_transports_sem); + list_del(&trans->proc_link); + up_write(&rxrpc_proc_transports_sem); + __RXACCT(atomic_dec(&rxrpc_transport_count)); + + /* close the socket */ + if (trans->socket) { + trans->socket->sk->user_data = NULL; + sock_release(trans->socket); + trans->socket = NULL; + } + + kfree(trans); + + _leave(""); + +} /* end rxrpc_put_transport() */ + +/*****************************************************************************/ +/* + * add a service to a transport to be listened upon + */ +int rxrpc_add_service(struct rxrpc_transport *trans, struct rxrpc_service *newsrv) +{ + struct rxrpc_service *srv; + struct list_head *_p; + int ret = -EEXIST; + + _enter("%p{%hu},%p{%hu}",trans,trans->port,newsrv,newsrv->service_id); + + /* verify that the service ID is not already present */ + spin_lock(&trans->lock); + + list_for_each(_p,&trans->services) { + srv = list_entry(_p,struct rxrpc_service,link); + if (srv->service_id==newsrv->service_id) + goto out; + } + + /* okay - add the transport to the list */ + list_add_tail(&newsrv->link,&trans->services); + rxrpc_get_transport(trans); + ret = 0; + + out: + spin_unlock(&trans->lock); + + _leave("= %d",ret); + return ret; + +} /* end rxrpc_add_service() */ + +/*****************************************************************************/ +/* + * remove a service from a transport + */ +void rxrpc_del_service(struct rxrpc_transport *trans, struct rxrpc_service *srv) +{ + _enter("%p{%hu},%p{%hu}",trans,trans->port,srv,srv->service_id); + + spin_lock(&trans->lock); + list_del(&srv->link); + spin_unlock(&trans->lock); + + rxrpc_put_transport(trans); + + _leave(""); + +} /* end rxrpc_del_service() */ + +/*****************************************************************************/ +/* + * INET callback when data has been received on the socket. + */ +static void rxrpc_data_ready(struct sock *sk, int count) +{ + struct rxrpc_transport *trans; + + _enter("%p{t=%p},%d",sk,sk->user_data,count); + + /* queue the transport for attention by krxiod */ + trans = (struct rxrpc_transport *) sk->user_data; + if (trans) + rxrpc_krxiod_queue_transport(trans); + + /* wake up anyone waiting on the socket */ + if (sk->sleep && waitqueue_active(sk->sleep)) + wake_up_interruptible(sk->sleep); + + _leave(""); + +} /* end rxrpc_data_ready() */ + +/*****************************************************************************/ +/* + * INET callback when an ICMP error packet is received + * - sk->err is error (EHOSTUNREACH, EPROTO or EMSGSIZE) + */ +static void rxrpc_error_report(struct sock *sk) +{ + struct rxrpc_transport *trans; + + _enter("%p{t=%p}",sk,sk->user_data); + + /* queue the transport for attention by krxiod */ + trans = (struct rxrpc_transport *) sk->user_data; + if (trans) { + trans->error_rcvd = 1; + rxrpc_krxiod_queue_transport(trans); + } + + /* wake up anyone waiting on the socket */ + if (sk->sleep && waitqueue_active(sk->sleep)) + wake_up_interruptible(sk->sleep); + + _leave(""); + +} /* end rxrpc_error_report() */ + +/*****************************************************************************/ +/* + * split a message up, allocating message records and filling them in from the contents of a + * socket buffer + */ +static int rxrpc_incoming_msg(struct rxrpc_transport *trans, + struct sk_buff *pkt, + struct list_head *msgq) +{ + struct rxrpc_message *msg; + int ret; + + _enter(""); + + msg = kmalloc(sizeof(struct rxrpc_message),GFP_KERNEL); + if (!msg) { + _leave(" = -ENOMEM"); + return -ENOMEM; + } + + memset(msg,0,sizeof(*msg)); + atomic_set(&msg->usage,1); + list_add_tail(&msg->link,msgq); + + /* dig out the Rx routing parameters */ + if (skb_copy_bits(pkt,sizeof(struct udphdr),&msg->hdr,sizeof(msg->hdr))<0) { + ret = -EBADMSG; + goto error; + } + + msg->trans = trans; + msg->state = RXRPC_MSG_RECEIVED; + msg->stamp = pkt->stamp; + msg->seq = ntohl(msg->hdr.seq); + + /* attach the packet */ + skb_get(pkt); + msg->pkt = pkt; + + msg->offset = sizeof(struct udphdr) + sizeof(struct rxrpc_header); + msg->dsize = msg->pkt->len - msg->offset; + + _net("Rx Received packet from %s (%08x;%08x,%1x,%d,%s,%02x,%d,%d)", + msg->hdr.flags & RXRPC_CLIENT_INITIATED ? "client" : "server", + ntohl(msg->hdr.epoch), + (ntohl(msg->hdr.cid) & RXRPC_CIDMASK) >> RXRPC_CIDSHIFT, + ntohl(msg->hdr.cid) & RXRPC_CHANNELMASK, + ntohl(msg->hdr.callNumber), + rxrpc_pkts[msg->hdr.type], + msg->hdr.flags, + ntohs(msg->hdr.serviceId), + msg->hdr.securityIndex); + + __RXACCT(atomic_inc(&rxrpc_message_count)); + + /* split off jumbo packets */ + while (msg->hdr.type==RXRPC_PACKET_TYPE_DATA && msg->hdr.flags & RXRPC_JUMBO_PACKET) { + struct rxrpc_jumbo_header jumbo; + struct rxrpc_message *jumbomsg = msg; + + _debug("split jumbo packet"); + + /* quick sanity check */ + ret = -EBADMSG; + if (msg->dsize < RXRPC_JUMBO_DATALEN+sizeof(struct rxrpc_jumbo_header)) + goto error; + if (msg->hdr.flags & RXRPC_LAST_PACKET) + goto error; + + /* dig out the secondary header */ + if (skb_copy_bits(pkt,msg->offset+RXRPC_JUMBO_DATALEN,&jumbo,sizeof(jumbo))<0) + goto error; + + /* allocate a new message record */ + ret = -ENOMEM; + msg = kmalloc(sizeof(struct rxrpc_message),GFP_KERNEL); + if (!msg) + goto error; + + memcpy(msg,jumbomsg,sizeof(*msg)); + list_add_tail(&msg->link,msgq); + + /* adjust the jumbo packet */ + jumbomsg->dsize = RXRPC_JUMBO_DATALEN; + + /* attach the packet here too */ + skb_get(pkt); + + /* adjust the parameters */ + msg->seq++; + msg->hdr.seq = htonl(msg->seq); + msg->hdr.serial = htonl(ntohl(msg->hdr.serial) + 1); + msg->offset += RXRPC_JUMBO_DATALEN + sizeof(struct rxrpc_jumbo_header); + msg->dsize -= RXRPC_JUMBO_DATALEN + sizeof(struct rxrpc_jumbo_header); + msg->hdr.flags = jumbo.flags; + msg->hdr._rsvd = jumbo._rsvd; + + _net("Rx Split jumbo packet from %s (%08x;%08x,%1x,%d,%s,%02x,%d,%d)", + msg->hdr.flags & RXRPC_CLIENT_INITIATED ? "client" : "server", + ntohl(msg->hdr.epoch), + (ntohl(msg->hdr.cid) & RXRPC_CIDMASK) >> RXRPC_CIDSHIFT, + ntohl(msg->hdr.cid) & RXRPC_CHANNELMASK, + ntohl(msg->hdr.callNumber), + rxrpc_pkts[msg->hdr.type], + msg->hdr.flags, + ntohs(msg->hdr.serviceId), + msg->hdr.securityIndex); + + __RXACCT(atomic_inc(&rxrpc_message_count)); + } + + _leave(" = 0 #%d",atomic_read(&rxrpc_message_count)); + return 0; + + error: + while (!list_empty(msgq)) { + msg = list_entry(msgq->next,struct rxrpc_message,link); + list_del_init(&msg->link); + + rxrpc_put_message(msg); + } + + _leave(" = %d",ret); + return ret; +} /* end rxrpc_incoming_msg() */ + +/*****************************************************************************/ +/* + * accept a new call + * - called from krxiod in process context + */ +void rxrpc_trans_receive_packet(struct rxrpc_transport *trans) +{ + struct rxrpc_message *msg; + struct rxrpc_peer *peer; + struct sk_buff *pkt; + int ret; + u32 addr; + u16 port; + + LIST_HEAD(msgq); + + _enter("%p{%d}",trans,trans->port); + + for (;;) { + /* deal with outstanting errors first */ + if (trans->error_rcvd) + rxrpc_trans_receive_error_report(trans); + + /* attempt to receive a packet */ + pkt = skb_recv_datagram(trans->socket->sk,0,1,&ret); + if (!pkt) { + if (ret==-EAGAIN) { + _leave(" EAGAIN"); + return; + } + + /* an icmp error may have occurred */ + rxrpc_krxiod_queue_transport(trans); + _leave(" error %d\n",ret); + return; + } + + /* we'll probably need to checksum it (didn't call sock_recvmsg) */ + if (pkt->ip_summed != CHECKSUM_UNNECESSARY) { + if ((unsigned short)csum_fold(skb_checksum(pkt,0,pkt->len,pkt->csum))) { + kfree_skb(pkt); + rxrpc_krxiod_queue_transport(trans); + _leave(" CSUM failed"); + return; + } + } + + addr = pkt->nh.iph->saddr; + port = pkt->h.uh->source; + + _net("Rx Received UDP packet from %08x:%04hu",ntohl(addr),ntohs(port)); + + /* unmarshall the Rx parameters and split jumbo packets */ + ret = rxrpc_incoming_msg(trans,pkt,&msgq); + if (ret<0) { + kfree_skb(pkt); + rxrpc_krxiod_queue_transport(trans); + _leave(" bad packet"); + return; + } + + if (list_empty(&msgq)) BUG(); + + msg = list_entry(msgq.next,struct rxrpc_message,link); + + /* locate the record for the peer from which it originated */ + ret = rxrpc_peer_lookup(trans,addr,&peer); + if (ret<0) { + kdebug("Rx No connections from that peer"); + rxrpc_trans_immediate_abort(trans,msg,-EINVAL); + goto finished_msg; + } + + /* try and find a matching connection */ + ret = rxrpc_connection_lookup(peer,msg,&msg->conn); + if (ret<0) { + kdebug("Rx Unknown Connection"); + rxrpc_trans_immediate_abort(trans,msg,-EINVAL); + rxrpc_put_peer(peer); + goto finished_msg; + } + rxrpc_put_peer(peer); + + /* deal with the first packet of a new call */ + if (msg->hdr.flags & RXRPC_CLIENT_INITIATED && + msg->hdr.type==RXRPC_PACKET_TYPE_DATA && + ntohl(msg->hdr.seq)==1 + ) { + _debug("Rx New server call"); + rxrpc_trans_receive_new_call(trans,&msgq); + goto finished_msg; + } + + /* deal with subsequent packet(s) of call */ + _debug("Rx Call packet"); + while (!list_empty(&msgq)) { + msg = list_entry(msgq.next,struct rxrpc_message,link); + list_del_init(&msg->link); + + ret = rxrpc_conn_receive_call_packet(msg->conn,NULL,msg); + if (ret<0) { + rxrpc_trans_immediate_abort(trans,msg,ret); + rxrpc_put_message(msg); + goto finished_msg; + } + + rxrpc_put_message(msg); + } + + goto finished_msg; + + /* dispose of the packets */ + finished_msg: + while (!list_empty(&msgq)) { + msg = list_entry(msgq.next,struct rxrpc_message,link); + list_del_init(&msg->link); + + rxrpc_put_message(msg); + } + kfree_skb(pkt); + } + + _leave(""); + +} /* end rxrpc_trans_receive_packet() */ + +/*****************************************************************************/ +/* + * accept a new call from a client trying to connect to one of my services + * - called in process context + */ +static int rxrpc_trans_receive_new_call(struct rxrpc_transport *trans, + struct list_head *msgq) +{ + struct rxrpc_message *msg; + + _enter(""); + + /* only bother with the first packet */ + msg = list_entry(msgq->next,struct rxrpc_message,link); + list_del_init(&msg->link); + rxrpc_krxsecd_queue_incoming_call(msg); + rxrpc_put_message(msg); + + _leave(" = 0"); + + return 0; +} /* end rxrpc_trans_receive_new_call() */ + +/*****************************************************************************/ +/* + * perform an immediate abort without connection or call structures + */ +int rxrpc_trans_immediate_abort(struct rxrpc_transport *trans, + struct rxrpc_message *msg, + int error) +{ + struct rxrpc_header ahdr; + struct sockaddr_in sin; + struct msghdr msghdr; + struct iovec iov[2]; + mm_segment_t oldfs; + int len, ret; + u32 _error; + + _enter("%p,%p,%d",trans,msg,error); + + /* don't abort an abort packet */ + if (msg->hdr.type==RXRPC_PACKET_TYPE_ABORT) { + _leave(" = 0"); + return 0; + } + + _error = htonl(-error); + + /* set up the message to be transmitted */ + memcpy(&ahdr,&msg->hdr,sizeof(ahdr)); + ahdr.epoch = msg->hdr.epoch; + ahdr.serial = htonl(1); + ahdr.seq = 0; + ahdr.type = RXRPC_PACKET_TYPE_ABORT; + ahdr.flags = RXRPC_LAST_PACKET | (~msg->hdr.flags & RXRPC_CLIENT_INITIATED); + + iov[0].iov_len = sizeof(ahdr); + iov[0].iov_base = &ahdr; + iov[1].iov_len = sizeof(_error); + iov[1].iov_base = &_error; + + len = sizeof(ahdr) + sizeof(_error); + + memset(&sin,0,sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = msg->pkt->h.uh->source; + sin.sin_addr.s_addr = msg->pkt->nh.iph->saddr; + + msghdr.msg_name = &sin; + msghdr.msg_namelen = sizeof(sin); + msghdr.msg_iov = iov; + msghdr.msg_iovlen = 2; + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + msghdr.msg_flags = MSG_DONTWAIT; + + _net("Sending message type %d of %d bytes to %08x:%d", + ahdr.type, + len, + htonl(sin.sin_addr.s_addr), + htons(sin.sin_port)); + + /* send the message */ + oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = sock_sendmsg(trans->socket,&msghdr,len); + set_fs(oldfs); + + _leave(" = %d",ret); + return ret; +} /* end rxrpc_trans_immediate_abort() */ + +/*****************************************************************************/ +/* + * receive an ICMP error report and percolate it to all connections heading to the affected + * host or port + */ +static void rxrpc_trans_receive_error_report(struct rxrpc_transport *trans) +{ + struct rxrpc_connection *conn; + struct sockaddr_in sin; + struct rxrpc_peer *peer; + struct list_head connq, *_p; + struct errormsg emsg; + struct msghdr msg; + mm_segment_t oldfs; + int local, err; + u16 port; + + _enter("%p",trans); + + for (;;) { + trans->error_rcvd = 0; + + /* try and receive an error message */ + msg.msg_name = &sin; + msg.msg_namelen = sizeof(sin); + msg.msg_iov = NULL; + msg.msg_iovlen = 0; + msg.msg_control = &emsg; + msg.msg_controllen = sizeof(emsg); + msg.msg_flags = 0; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = sock_recvmsg(trans->socket,&msg,0,MSG_ERRQUEUE|MSG_DONTWAIT|MSG_TRUNC); + set_fs(oldfs); + + if (err==-EAGAIN) { + _leave(""); + return; + } + + if (err<0) { + printk("%s: unable to recv an error report: %d\n",__FUNCTION__,err); + _leave(""); + return; + } + + msg.msg_controllen = (char*)msg.msg_control - (char*)&emsg; + + if (msg.msg_controllenconn_lock); + list_for_each(_p,&peer->conn_active) { + conn = list_entry(_p,struct rxrpc_connection,link); + if (port && conn->addr.sin_port!=port) + continue; + if (!list_empty(&conn->err_link)) + continue; + + rxrpc_get_connection(conn); + list_add_tail(&conn->err_link,&connq); + } + read_unlock(&peer->conn_lock); + + /* service all those connections */ + while (!list_empty(&connq)) { + conn = list_entry(connq.next,struct rxrpc_connection,err_link); + list_del(&conn->err_link); + + rxrpc_conn_handle_error(conn,local,err); + + rxrpc_put_connection(conn); + } + + rxrpc_put_peer(peer); + } + } + + _leave(""); + return; +} /* end rxrpc_trans_receive_error_report() */ -- cgit v1.2.3