Short enough reads from /proc/irq/*/smp_affinity return -EINVAL for no good reason. This became noticed with NR_CPUS=4096 patches, when length of printed representation of cpumask becase 1152, but cat(1) continued to read with 1024-byte chunks. bitmap_scnprintf() in good faith fills buffer, returns 1023, check returns -EINVAL. Fix it by switching to seq_file, so handler will just fill buffer and doesn't care about offsets, length, filling EOF and all this crap. For that add seq_bitmap(), and wrappers around it -- seq_cpumask() and seq_nodemask().
Unified · Split
| @@ -443,6 +443,20 @@ int seq_dentry(struct seq_file *m, struct dentry *dentry, char *esc) · 442 unmodified lines | ||
| 443 | 443 | return -1; |
| 444 | 444 | } |
| 445 | 445 | |
| 446 | +int seq_bitmap(struct seq_file *m, unsigned long *bits, unsigned int nr_bits) | |
| 447 | +{ | |
| 448 | + size_t len = bitmap_scnprintf_len(nr_bits); | |
| 449 | + | |
| 450 | + if (m->count + len < m->size) { | |
| 451 | + bitmap_scnprintf(m->buf + m->count, m->size - m->count, | |
| 452 | + bits, nr_bits); | |
| 453 | + m->count += len; | |
| 454 | + return 0; | |
| 455 | + } | |
| 456 | + m->count = m->size; | |
| 457 | + return -1; | |
| 458 | +} | |
| 459 | + | |
| 446 | 460 | static void *single_start(struct seq_file *p, loff_t *pos) |
| 447 | 461 | { |
| 448 | 462 | return NULL + (*pos == 0); |
| @@ -110,6 +110,7 @@ extern int __bitmap_weight(const unsigned long *bitmap, int bits); · 109 unmodified lines | ||
| 110 | 110 | |
| 111 | 111 | extern int bitmap_scnprintf(char *buf, unsigned int len, |
| 112 | 112 | const unsigned long *src, int nbits); |
| 113 | +extern int bitmap_scnprintf_len(unsigned int nr_bits); | |
| 113 | 114 | extern int __bitmap_parse(const char *buf, unsigned int buflen, int is_user, |
| 114 | 115 | unsigned long *dst, int nbits); |
| 115 | 116 | extern int bitmap_parse_user(const char __user *ubuf, unsigned int ulen, |
| @@ -4,6 +4,8 @@ · 3 unmodified lines | ||
| 4 | 4 | #include <linux/types.h> |
| 5 | 5 | #include <linux/string.h> |
| 6 | 6 | #include <linux/mutex.h> |
| 7 | +#include <linux/cpumask.h> | |
| 8 | +#include <linux/nodemask.h> | |
| 7 | 9 | |
| 8 | 10 | struct seq_operations; |
| 9 | 11 | struct file; |
| @@ -47,6 +49,16 @@ int seq_path(struct seq_file *, struct path *, char *); · 37 unmodified lines | ||
| 47 | 49 | int seq_dentry(struct seq_file *, struct dentry *, char *); |
| 48 | 50 | int seq_path_root(struct seq_file *m, struct path *path, struct path *root, |
| 49 | 51 | char *esc); |
| 52 | +int seq_bitmap(struct seq_file *m, unsigned long *bits, unsigned int nr_bits); | |
| 53 | +static inline int seq_cpumask(struct seq_file *m, cpumask_t *mask) | |
| 54 | +{ | |
| 55 | + return seq_bitmap(m, mask->bits, NR_CPUS); | |
| 56 | +} | |
| 57 | + | |
| 58 | +static inline int seq_nodemask(struct seq_file *m, nodemask_t *mask) | |
| 59 | +{ | |
| 60 | + return seq_bitmap(m, mask->bits, MAX_NUMNODES); | |
| 61 | +} | |
| 50 | 62 | |
| 51 | 63 | int single_open(struct file *, int (*)(struct seq_file *, void *), void *); |
| 52 | 64 | int single_release(struct inode *, struct file *); |
| @@ -315,6 +315,17 @@ int bitmap_scnprintf(char *buf, unsigned int buflen, · 314 unmodified lines | ||
| 315 | 315 | } |
| 316 | 316 | EXPORT_SYMBOL(bitmap_scnprintf); |
| 317 | 317 | |
| 318 | +/** | |
| 319 | + * bitmap_scnprintf_len - return buffer length needed to convert | |
| 320 | + * bitmap to an ASCII hex string | |
| 321 | + * @nr_bits: number of bits to be converted | |
| 322 | + */ | |
| 323 | +int bitmap_scnprintf_len(unsigned int nr_bits) | |
| 324 | +{ | |
| 325 | + unsigned int nr_nibbles = ALIGN(nr_bits, 4) / 4; | |
| 326 | + return nr_nibbles + ALIGN(nr_nibbles, CHUNKSZ / 4) / (CHUNKSZ / 4) - 1; | |
| 327 | +} | |
| 328 | + | |
| 318 | 329 | /** |
| 319 | 330 | * __bitmap_parse - convert an ASCII hex string into a bitmap. |
| 320 | 331 | * @buf: pointer to buffer containing string. |