Skip to contentSkip to conversation

handle failure of irqchip->set_type in setup_irq

PATCH Superseded Uwe Kleine-König 15 participants 52 messages Jun 25, 2008 – Jul 22, 2008 2 messages not in the archive

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 handle failure of irqchip->set_type in setup_irq

set_type returns an int but currently setup_irq ignores that.

To save me from undoing some changes setting the IRQ_NO_BALANCING bit in desc->flags, setting IRQ_PER_CPU in desc->status and adding the new action to desc is only done after set_type succeeded.

---
Hello,

in my case set_type returns an error because gpio-key tries to use IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING and my irq-chip can only do one of these.

Best regards
Uwe

 kernel/irq/manage.c |   38 ++++++++++++++++++++++++--------------
 1 files changed, 24 insertions(+), 14 deletions(-)
kernel/irq/manage.c +24 -14
index 46d6611..bc990a1 100644
@@ -281,6 +281,7 @@ int setup_irq(unsigned int irq, struct irqaction *new)
 	const char *old_name = NULL;
 	unsigned long flags;
 	int shared = 0;
+	int ret;
 
 	if (irq >= NR_IRQS)
 		return -EINVAL;
@@ -338,26 +339,23 @@ int setup_irq(unsigned int irq, struct irqaction *new)
 		shared = 1;
 	}
 
-	*p = new;
-
-	/* Exclude IRQ from balancing */
-	if (new->flags & IRQF_NOBALANCING)
-		desc->status |= IRQ_NO_BALANCING;
-
 	if (!shared) {
 		irq_chip_set_defaults(desc->chip);
 
-#if defined(CONFIG_IRQ_PER_CPU)
-		if (new->flags & IRQF_PERCPU)
-			desc->status |= IRQ_PER_CPU;
-#endif
-
 		/* Setup the type (level, edge polarity) if configured: */
 		if (new->flags & IRQF_TRIGGER_MASK) {
-			if (desc->chip && desc->chip->set_type)
-				desc->chip->set_type(irq,
+			if (desc->chip && desc->chip->set_type) {
+				ret = desc->chip->set_type(irq,
 						new->flags & IRQF_TRIGGER_MASK);
-			else
+				if (ret) {
+					pr_err("setting flow type for irq %u "
+							"failed\n", irq);
+					spin_unlock_irqrestore(&desc->lock,
+							flags);
+					return ret;
+				}
+
+			} else
 				/*
 				 * IRQF_TRIGGER_* but the PIC does not support
 				 * multiple flow-types?
@@ -369,6 +367,11 @@ int setup_irq(unsigned int irq, struct irqaction *new)
 		} else
 			compat_irq_chip_set_default_handler(desc);
 
+#if defined(CONFIG_IRQ_PER_CPU)
+		if (new->flags & IRQF_PERCPU)
+			desc->status |= IRQ_PER_CPU;
+#endif
+
 		desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
 				  IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
 
@@ -383,6 +386,13 @@ int setup_irq(unsigned int irq, struct irqaction *new)
 			/* Undo nested disables: */
 			desc->depth = 1;
 	}
+
+	*p = new;
+
+	/* Exclude IRQ from balancing */
+	if (new->flags & IRQF_NOBALANCING)
+		desc->status |= IRQ_NO_BALANCING;
+
 	/* Reset broken irq detection when installing new handler */
 	desc->irq_count = 0;
 	desc->irqs_unhandled = 0;
Signature · 7 lines
1.5.6


-- 
Uwe Kleine-König, Software Engineer
Digi International GmbH Branch Breisach, Küferstrasse 8, 79206 Breisach, Germany
Tax: 315/5781/0242 / VAT: DE153662976 / Reg. Amtsgericht Dortmund HRB 13962
#1 lore raw

Hello,

[extending the Cc: list]

Quoted 6 lines · Uwe Kleine-König

set_type returns an int but currently setup_irq ignores that.

To save me from undoing some changes setting the IRQ_NO_BALANCING bit in desc->flags, setting IRQ_PER_CPU in desc->status and adding the new action to desc is only done after set_type succeeded.

Quoted 7 lines

---
Hello,

in my case set_type returns an error because gpio-key tries to use IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING and my irq-chip can only do one of these.

up to now I didn't get any response for this patch. It addresses a real problem for my platform so I really like to have a fix in.

In mmotm of 2008-07-01-21-57 kernel/irq/manage.c is touched[1], too, but git was able to automerge the change correctly when applying it on top of mmotm.

For your convenience you can find the patch again at the end of this mail.

Andrew: Do you can take this patch into mm?

Best regards
Uwe

[1] kernel/irq/manage.c is modified by linux-next patch. The relevant commit is:
1840475... genirq: Expose default irq affinity mask (take 3)

Quoted 76 lines
kernel/irq/manage.c +24 -14
index 46d6611..bc990a1 100644
@@ -281,6 +281,7 @@ int setup_irq(unsigned int irq, struct irqaction *new)
 	const char *old_name = NULL;
 	unsigned long flags;
 	int shared = 0;
+	int ret;
 
 	if (irq >= NR_IRQS)
 		return -EINVAL;
@@ -338,26 +339,23 @@ int setup_irq(unsigned int irq, struct irqaction *new)
 		shared = 1;
 	}
 
-	*p = new;
-
-	/* Exclude IRQ from balancing */
-	if (new->flags & IRQF_NOBALANCING)
-		desc->status |= IRQ_NO_BALANCING;
-
 	if (!shared) {
 		irq_chip_set_defaults(desc->chip);
 
-#if defined(CONFIG_IRQ_PER_CPU)
-		if (new->flags & IRQF_PERCPU)
-			desc->status |= IRQ_PER_CPU;
-#endif
-
 		/* Setup the type (level, edge polarity) if configured: */
 		if (new->flags & IRQF_TRIGGER_MASK) {
-			if (desc->chip && desc->chip->set_type)
-				desc->chip->set_type(irq,
+			if (desc->chip && desc->chip->set_type) {
+				ret = desc->chip->set_type(irq,
 						new->flags & IRQF_TRIGGER_MASK);
-			else
+				if (ret) {
+					pr_err("setting flow type for irq %u "
+							"failed\n", irq);
+					spin_unlock_irqrestore(&desc->lock,
+							flags);
+					return ret;
+				}
+
+			} else
 				/*
 				 * IRQF_TRIGGER_* but the PIC does not support
 				 * multiple flow-types?
@@ -369,6 +367,11 @@ int setup_irq(unsigned int irq, struct irqaction *new)
 		} else
 			compat_irq_chip_set_default_handler(desc);
 
+#if defined(CONFIG_IRQ_PER_CPU)
+		if (new->flags & IRQF_PERCPU)
+			desc->status |= IRQ_PER_CPU;
+#endif
+
 		desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
 				  IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
 
@@ -383,6 +386,13 @@ int setup_irq(unsigned int irq, struct irqaction *new)
 			/* Undo nested disables: */
 			desc->depth = 1;
 	}
+
+	*p = new;
+
+	/* Exclude IRQ from balancing */
+	if (new->flags & IRQF_NOBALANCING)
+		desc->status |= IRQ_NO_BALANCING;
+
 	/* Reset broken irq detection when installing new handler */
 	desc->irq_count = 0;
 	desc->irqs_unhandled = 0;
Signature · 3 lines
Uwe Kleine-König, Software Engineer
Digi International GmbH Branch Breisach, Küferstrasse 8, 79206 Breisach, Germany
Tax: 315/5781/0242 / VAT: DE153662976 / Reg. Amtsgericht Dortmund HRB 13962
maintainer
#2 lore raw
Quoted 26 lines · On Wed, 2 Jul 2008 11:17:57 +0200 Uwe Kleine-K__nig…

Hello,

[extending the Cc: list]

Quoted 6 lines · Uwe Kleine-K__nig

set_type returns an int but currently setup_irq ignores that.

To save me from undoing some changes setting the IRQ_NO_BALANCING bit in desc->flags, setting IRQ_PER_CPU in desc->status and adding the new action to desc is only done after set_type succeeded.

Quoted 7 lines

---
Hello,

in my case set_type returns an error because gpio-key tries to use IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING and my irq-chip can only do one of these.

up to now I didn't get any response for this patch. It addresses a real problem for my platform so I really like to have a fix in.

In mmotm of 2008-07-01-21-57 kernel/irq/manage.c is touched[1], too, but git was able to automerge the change correctly when applying it on top of mmotm.

Quoted 7 lines

For your convenience you can find the patch again at the end of this mail.

Andrew: Do you can take this patch into mm?

From a brief squint the patch seems to be reasonable. But the

changelog is a bit mangled. Perhaps you could have another go when resending it. Explan more clearly under what circumststances your ->set_type() implementation can fail and why you require the core code to handle this.

Perhaps we want a dump_stack() on the error path so we can see who goofed. Or a print_symbol() of desc->chip->set_type. Or perhaps not.

Did you check that all the current ->set_type() implementations are returning zero?

Quoted 82 lines

[1] kernel/irq/manage.c is modified by linux-next patch. The relevant commit is:
1840475... genirq: Expose default irq affinity mask (take 3)

Quoted 76 lines
kernel/irq/manage.c +24 -14
index 46d6611..bc990a1 100644
@@ -281,6 +281,7 @@ int setup_irq(unsigned int irq, struct irqaction *new)
 	const char *old_name = NULL;
 	unsigned long flags;
 	int shared = 0;
+	int ret;
 
 	if (irq >= NR_IRQS)
 		return -EINVAL;
@@ -338,26 +339,23 @@ int setup_irq(unsigned int irq, struct irqaction *new)
 		shared = 1;
 	}
 
-	*p = new;
-
-	/* Exclude IRQ from balancing */
-	if (new->flags & IRQF_NOBALANCING)
-		desc->status |= IRQ_NO_BALANCING;
-
 	if (!shared) {
 		irq_chip_set_defaults(desc->chip);
 
-#if defined(CONFIG_IRQ_PER_CPU)
-		if (new->flags & IRQF_PERCPU)
-			desc->status |= IRQ_PER_CPU;
-#endif
-
 		/* Setup the type (level, edge polarity) if configured: */
 		if (new->flags & IRQF_TRIGGER_MASK) {
-			if (desc->chip && desc->chip->set_type)
-				desc->chip->set_type(irq,
+			if (desc->chip && desc->chip->set_type) {
+				ret = desc->chip->set_type(irq,
 						new->flags & IRQF_TRIGGER_MASK);
-			else
+				if (ret) {
+					pr_err("setting flow type for irq %u "
+							"failed\n", irq);
+					spin_unlock_irqrestore(&desc->lock,
+							flags);
+					return ret;
+				}
+
+			} else
 				/*
 				 * IRQF_TRIGGER_* but the PIC does not support
 				 * multiple flow-types?
@@ -369,6 +367,11 @@ int setup_irq(unsigned int irq, struct irqaction *new)
 		} else
 			compat_irq_chip_set_default_handler(desc);
 
+#if defined(CONFIG_IRQ_PER_CPU)
+		if (new->flags & IRQF_PERCPU)
+			desc->status |= IRQ_PER_CPU;
+#endif
+
 		desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
 				  IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
 
@@ -383,6 +386,13 @@ int setup_irq(unsigned int irq, struct irqaction *new)
 			/* Undo nested disables: */
 			desc->depth = 1;
 	}
+
+	*p = new;
+
+	/* Exclude IRQ from balancing */
+	if (new->flags & IRQF_NOBALANCING)
+		desc->status |= IRQ_NO_BALANCING;
+
 	/* Reset broken irq detection when installing new handler */
 	desc->irq_count = 0;
 	desc->irqs_unhandled = 0;
#3 lore raw

Hello Andrew,

Quoted 31 lines · Andrew Morton
Quoted 29 lines · On Wed, 2 Jul 2008 11:17:57 +0200 Uwe Kleine-K__nig…

Hello,

[extending the Cc: list]

Quoted 6 lines · Uwe Kleine-K__nig

set_type returns an int but currently setup_irq ignores that.

To save me from undoing some changes setting the IRQ_NO_BALANCING bit in desc->flags, setting IRQ_PER_CPU in desc->status and adding the new action to desc is only done after set_type succeeded.

Quoted 7 lines

---
Hello,

in my case set_type returns an error because gpio-key tries to use IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING and my irq-chip can only do one of these.

up to now I didn't get any response for this patch. It addresses a real problem for my platform so I really like to have a fix in.

In mmotm of 2008-07-01-21-57 kernel/irq/manage.c is touched[1], too, but git was able to automerge the change correctly when applying it on top of mmotm.

For your convenience you can find the patch again at the end of this mail.

Quoted 8 lines
Quoted 4 lines

Andrew: Do you can take this patch into mm?

From a brief squint the patch seems to be reasonable. But the

changelog is a bit mangled. Perhaps you could have another go when resending it. Explan more clearly under what circumststances your ->set_type() implementation can fail and why you require the core code to handle this.

OK.

Quoted 2 lines

Perhaps we want a dump_stack() on the error path so we can see who goofed. Or a print_symbol() of desc->chip->set_type. Or perhaps not.

I thought about a dump_stack() but considered it as too much. print_symbol() is a nice idea though.

Quoted 2 lines

Did you check that all the current ->set_type() implementations are returning zero?

No, I don't. But for example orion5x_gpio_set_irq_type might return -EINVAL. txx9_irq_set_type seems to have the same limitation as ns9xxx and returns -EINVAL if more than one trigger type is requested.

Best regards
Uwe

Signature · 3 lines
Uwe Kleine-König, Software Engineer
Digi International GmbH Branch Breisach, Küferstrasse 8, 79206 Breisach, Germany
Tax: 315/5781/0242 / VAT: DE153662976 / Reg. Amtsgericht Dortmund HRB 13962
#4 lore raw

PATCH v2 1/1 handle failure of irqchip->set_type in setup_irq

In reply to Uwe Kleine-König

set_type returns an int indicating success or failure, but up to now setup_irq ignores that.

In my case this resulted in a machine hang:
gpio-keys requested IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, but arm/ns9xxx can only trigger on one direction so set_type didn't touch the configuration which happens do default to a level sensitiveness and returned -EINVAL. setup_irq ignored that and unmasked the irq. This resulted in an endless triggering of the gpio-key interrupt service routine which effectively killed the machine.

With this patch applied setup_irq propagates the error to the caller.

Note that before in the case

	chip && !chip->set_type && !chip->name

a NULL pointer was feed to printk. This is fixed, too.

---
Hello,

Changes since initial post:

- improve commit log (hopefully)
- move code to a dedicated function to improve readability and code line length.
- include the symbolic name of the failing callback in the error message.

Best regards
Uwe

 kernel/irq/manage.c |   72 ++++++++++++++++++++++++++++++++++----------------
 1 files changed, 49 insertions(+), 23 deletions(-)
kernel/irq/manage.c +49 -23
index 46d6611..178b966 100644
@@ -270,6 +270,35 @@ void compat_irq_chip_set_default_handler(struct irq_desc *desc)
 		desc->handle_irq = NULL;
 }
 
+static int __irq_set_trigger(struct irq_chip *chip, unsigned int irq,
+		unsigned long flags)
+{
+	int ret;
+
+	if (!chip || !chip->set_type) {
+		/*
+		 * IRQF_TRIGGER_* but the PIC does not support multiple
+		 * flow-types?
+		 */
+		pr_warning("No set_type function for IRQ %d (%s)\n", irq,
+				chip ? (chip->name ? : "unknown") : "unknown");
+		return 0;
+	}
+
+	ret = chip->set_type(irq, flags & IRQF_TRIGGER_MASK);
+
+	if (ret) {
+		char buf[100];
+
+		snprintf(buf, sizeof(buf), KERN_ERR
+				"setting flow type for irq %u failed (%%s)\n",
+				irq);
+		print_fn_descriptor_symbol(buf, chip->set_type);
+	}
+
+	return ret;
+}
+
 /*
  * Internal function to register an irqaction - typically used to
  * allocate special interrupts that are part of the architecture.
@@ -281,6 +310,7 @@ int setup_irq(unsigned int irq, struct irqaction *new)
 	const char *old_name = NULL;
 	unsigned long flags;
 	int shared = 0;
+	int ret;
 
 	if (irq >= NR_IRQS)
 		return -EINVAL;
@@ -338,37 +368,26 @@ int setup_irq(unsigned int irq, struct irqaction *new)
 		shared = 1;
 	}
 
-	*p = new;
-
-	/* Exclude IRQ from balancing */
-	if (new->flags & IRQF_NOBALANCING)
-		desc->status |= IRQ_NO_BALANCING;
-
 	if (!shared) {
 		irq_chip_set_defaults(desc->chip);
 
-#if defined(CONFIG_IRQ_PER_CPU)
-		if (new->flags & IRQF_PERCPU)
-			desc->status |= IRQ_PER_CPU;
-#endif
-
 		/* Setup the type (level, edge polarity) if configured: */
 		if (new->flags & IRQF_TRIGGER_MASK) {
-			if (desc->chip && desc->chip->set_type)
-				desc->chip->set_type(irq,
-						new->flags & IRQF_TRIGGER_MASK);
-			else
-				/*
-				 * IRQF_TRIGGER_* but the PIC does not support
-				 * multiple flow-types?
-				 */
-				printk(KERN_WARNING "No IRQF_TRIGGER set_type "
-				       "function for IRQ %d (%s)\n", irq,
-				       desc->chip ? desc->chip->name :
-				       "unknown");
+			ret = __irq_set_trigger(desc->chip, irq, new->flags);
+
+			if (ret) {
+				spin_unlock_irqrestore(&desc->lock, flags);
+				return ret;
+			}
+
 		} else
 			compat_irq_chip_set_default_handler(desc);
 
+#if defined(CONFIG_IRQ_PER_CPU)
+		if (new->flags & IRQF_PERCPU)
+			desc->status |= IRQ_PER_CPU;
+#endif
+
 		desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
 				  IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
 
@@ -383,6 +402,13 @@ int setup_irq(unsigned int irq, struct irqaction *new)
 			/* Undo nested disables: */
 			desc->depth = 1;
 	}
+
+	*p = new;
+
+	/* Exclude IRQ from balancing */
+	if (new->flags & IRQF_NOBALANCING)
+		desc->status |= IRQ_NO_BALANCING;
+
 	/* Reset broken irq detection when installing new handler */
 	desc->irq_count = 0;
 	desc->irqs_unhandled = 0;
Signature · 1 line
1.5.6
maintainer
#5 lore raw
Quoted 11 lines · On Fri, 4 Jul 2008 12:46:34 +0200 Uwe Kleine-K__nig…

set_type returns an int indicating success or failure, but up to now setup_irq ignores that.

In my case this resulted in a machine hang:
gpio-keys requested IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, but arm/ns9xxx can only trigger on one direction so set_type didn't touch the configuration which happens do default to a level sensitiveness and returned -EINVAL. setup_irq ignored that and unmasked the irq. This resulted in an endless triggering of the gpio-key interrupt service routine which effectively killed the machine.

Quoted 8 lines

With this patch applied setup_irq propagates the error to the caller.

Note that before in the case

	chip && !chip->set_type && !chip->name

a NULL pointer was feed to printk. This is fixed, too.

hm, OK. Do I recall there being some urgency to this?

kernel/irq/manage.c void compat_irq_chip_set_default_handler(struct irq_desc *desc) · line 297

Quoted 8 lines
kernel/irq/manage.c +8 -0
+	if (ret) {
+		char buf[100];
+
+		snprintf(buf, sizeof(buf), KERN_ERR
+				"setting flow type for irq %u failed (%%s)\n",
+				irq);
+		print_fn_descriptor_symbol(buf, chip->set_type);
+	}

eww. We really mucked up that interface. I wonder if we can do better. Let me think about that.

#6 lore raw

Hello,

Quoted 15 lines · Andrew Morton
Quoted 13 lines · On Fri, 4 Jul 2008 12:46:34 +0200 Uwe Kleine-K__nig…

set_type returns an int indicating success or failure, but up to now setup_irq ignores that.

In my case this resulted in a machine hang:
gpio-keys requested IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, but arm/ns9xxx can only trigger on one direction so set_type didn't touch the configuration which happens do default to a level sensitiveness and returned -EINVAL. setup_irq ignored that and unmasked the irq. This resulted in an endless triggering of the gpio-key interrupt service routine which effectively killed the machine.

With this patch applied setup_irq propagates the error to the caller.

Quoted 8 lines
Quoted 6 lines

Note that before in the case

      chip && !chip->set_type && !chip->name

a NULL pointer was feed to printk. This is fixed, too.

hm, OK. Do I recall there being some urgency to this?

No, I didn't mention this before. (Fixing NULL-Pointers feed to printk is a bit of a reflex for me as I used to program for Solaris and if you feed a NULL-Pointer to printf there your program segfaults.) I didn't check that recently, but IIRC it's no problem here.

Quoted 11 lines

kernel/irq/manage.c void compat_irq_chip_set_default_handler(struct irq_desc *desc) · line 297

Quoted 9 lines
kernel/irq/manage.c +8 -0
+     if (ret) {
+             char buf[100];
+
+             snprintf(buf, sizeof(buf), KERN_ERR
+                             "setting flow type for irq %u failed (%%s)\n",
+                             irq);
+             print_fn_descriptor_symbol(buf, chip->set_type);
+     }

eww. We really mucked up that interface.

That's what I thought, too. ;-) This was the nicest way I found to print the whole line in one go.

Quoted 2 lines

I wonder if we can do better.
Let me think about that.

I was about to suggest something like:

	/* WARNING: this returns a static pointer, so you cannot use the
	 * returned value after another call to creative_function_name
	 */
	char *creative_function_name(void *addr)
	{
		static char buf[SOME_LENGTH];

		... format symbol name into buf ...

		return buf;
	}

Then I could have used

	pr_err("setting flow type for irq %u failed (%s)\n",
			irq, creative_function_name(chip->set_type));

which looks definitely nicer.

Thanks for taking the patch.

Best regards
Uwe

Signature · 3 lines
Uwe Kleine-König, Software Engineer
Digi International GmbH Branch Breisach, Küferstrasse 8, 79206 Breisach, Germany
Tax: 315/5781/0242 / VAT: DE153662976 / Reg. Amtsgericht Dortmund HRB 13962
maintainer
#7 lore raw
Quoted 16 lines · On Fri, 4 Jul 2008 20:43:07 +0200 Uwe Kleine-K__nig…
Quoted 2 lines

I wonder if we can do better.
Let me think about that.

I was about to suggest something like:

	/* WARNING: this returns a static pointer, so you cannot use the
	 * returned value after another call to creative_function_name
	 */
	char *creative_function_name(void *addr)
	{
		static char buf[SOME_LENGTH];

		... format symbol name into buf ...

		return buf;
	}
Quoted 6 lines

Then I could have used

	pr_err("setting flow type for irq %u failed (%s)\n",
			irq, creative_function_name(chip->set_type));

which looks definitely nicer.

Better would be

	char buf[ENOUGH /* rofl */];

	pr_err("setting flow type for irq %u failed (%s)\n",
			irq, render_function_name(buf, chip->set_type));

However I'm presently brewing up a plot to do this:

	printk("function name is %Ss\n", (unsigned long *)chip->set_type);

which I think will work. We can also do

	printk("IP address is %Si\n", (unsigned long *)ip_address_buffer);

to replace NIPQUAD. And similar printk extensions.

Am still thinking about it though.

#8 lore raw
Quoted 36 lines · Andrew Morton
Quoted 16 lines · On Fri, 4 Jul 2008 20:43:07 +0200 Uwe Kleine-K__nig…
Quoted 2 lines

I wonder if we can do better.
Let me think about that.

I was about to suggest something like:

      /* WARNING: this returns a static pointer, so you cannot use the
       * returned value after another call to creative_function_name
       */
      char *creative_function_name(void *addr)
      {
              static char buf[SOME_LENGTH];

              ... format symbol name into buf ...

              return buf;
      }
Quoted 6 lines

Then I could have used

pr_err("setting flow type for irq %u failed (%s)\n",
irq, creative_function_name(chip->set_type));

which looks definitely nicer.

Better would be

        char buf[ENOUGH /* rofl */];

        pr_err("setting flow type for irq %u failed (%s)\n",
                        irq, render_function_name(buf, chip->set_type));

However I'm presently brewing up a plot to do this:

        printk("function name is %Ss\n", (unsigned long *)chip->set_type);
Quoted 7 lines

which I think will work. We can also do

        printk("IP address is %Si\n", (unsigned long *)ip_address_buffer);

to replace NIPQUAD. And similar printk extensions.

Am still thinking about it though.

I assume the result is 0fe1ef24f7bd0020f29ffe287dfdb9ead33ca0b2?

Should I send a new version using %pF or is it easier if you fix up the patch?

Best regards
Uwe

Signature · 3 lines
Uwe Kleine-König, Software Engineer
Digi International GmbH Branch Breisach, Küferstrasse 8, 79206 Breisach, Germany
Tax: 315/5781/0242 / VAT: DE153662976 / Reg. Amtsgericht Dortmund HRB 13962
maintainer
#9 lore raw
Quoted 37 lines · On Wed, 9 Jul 2008 15:13:53 +0200 Uwe Kleine-K__nig…
Quoted 36 lines · Andrew Morton
Quoted 16 lines · On Fri, 4 Jul 2008 20:43:07 +0200 Uwe Kleine-K__nig…
Quoted 2 lines

I wonder if we can do better.
Let me think about that.

I was about to suggest something like:

      /* WARNING: this returns a static pointer, so you cannot use the
       * returned value after another call to creative_function_name
       */
      char *creative_function_name(void *addr)
      {
              static char buf[SOME_LENGTH];

              ... format symbol name into buf ...

              return buf;
      }
Quoted 6 lines

Then I could have used

pr_err("setting flow type for irq %u failed (%s)\n",
irq, creative_function_name(chip->set_type));

which looks definitely nicer.

Better would be

        char buf[ENOUGH /* rofl */];

        pr_err("setting flow type for irq %u failed (%s)\n",
                        irq, render_function_name(buf, chip->set_type));

However I'm presently brewing up a plot to do this:

        printk("function name is %Ss\n", (unsigned long *)chip->set_type);
Quoted 8 lines
Quoted 7 lines

which I think will work. We can also do

        printk("IP address is %Si\n", (unsigned long *)ip_address_buffer);

to replace NIPQUAD. And similar printk extensions.

Am still thinking about it though.

I assume the result is 0fe1ef24f7bd0020f29ffe287dfdb9ead33ca0b2?

yup.

Quoted 2 lines

Should I send a new version using %pF or is it easier if you fix up the patch?

A new version would be nice, thanks. Or an incremental diff, but I can and will turn new versions into an incremental in the twinkle of an eye, so either is OK.

#10 lore raw

Hello,

Quoted 6 lines · Andrew Morton
Quoted 6 lines
Quoted 6 lines

However I'm presently brewing up a plot to do this:

        printk("function name is %Ss\n", (unsigned long *)chip->set_type);

which I think will work. We can also do

Quoted 8 lines
Quoted 6 lines
Quoted 5 lines
        printk("IP address is %Si\n", (unsigned long *)ip_address_buffer);

to replace NIPQUAD. And similar printk extensions.

Am still thinking about it though.

I assume the result is 0fe1ef24f7bd0020f29ffe287dfdb9ead33ca0b2?

yup.

When I saw this commit I wondered that it was you who told me to think about this but the commiter was Linus.

Quoted 6 lines
Quoted 2 lines

Should I send a new version using %pF or is it easier if you fix up the patch?

A new version would be nice, thanks. Or an incremental diff, but I can and will turn new versions into an incremental in the twinkle of an eye, so either is OK.

voilà:

--->8---

Quoted 1 line

From c991997725fdce93352aab53ccab34f41b5afd52 Mon Sep 17 00:00:00 2001

From: Uwe Kleine-König <[email protected]>
Date: Thu, 10 Jul 2008 10:03:59 +0200
Subject: [PATCH] __irq_set_trigger: use %pF to print the set_type callback's name
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit

This uses the new feature of printk introduced in
0fe1ef24f7bd0020f29ffe287dfdb9ead33ca0b2.

The actual output is unchanged.

---
 kernel/irq/manage.c |   11 +++--------
 1 files changed, 3 insertions(+), 8 deletions(-)
kernel/irq/manage.c +3 -8
index 178b966..e01ad8e 100644
@@ -287,14 +287,9 @@ static int __irq_set_trigger(struct irq_chip *chip, unsigned int irq,
 
 	ret = chip->set_type(irq, flags & IRQF_TRIGGER_MASK);
 
-	if (ret) {
-		char buf[100];
-
-		snprintf(buf, sizeof(buf), KERN_ERR
-				"setting flow type for irq %u failed (%%s)\n",
-				irq);
-		print_fn_descriptor_symbol(buf, chip->set_type);
-	}
+	if (ret)
+		pr_err("setting flow type for irq %u failed (%pF)\n",
+				irq, chip->set_type);
 
 	return ret;
 }
Signature · 7 lines
1.5.6


-- 
Uwe Kleine-König, Software Engineer
Digi International GmbH Branch Breisach, Küferstrasse 8, 79206 Breisach, Germany
Tax: 315/5781/0242 / VAT: DE153662976 / Reg. Amtsgericht Dortmund HRB 13962
maintainer
#11 lore raw
Quoted 11 lines · On Thu, 10 Jul 2008 10:23:36 +0200 Uwe Kleine-K__nig…

Hello,

Quoted 8 lines · Andrew Morton
Quoted 8 lines
Quoted 8 lines

However I'm presently brewing up a plot to do this:

        printk("function name is %Ss\n", (unsigned long *)chip->set_type);

which I think will work. We can also do

        printk("IP address is %Si\n", (unsigned long *)ip_address_buffer);
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
Quoted 3 lines

to replace NIPQUAD. And similar printk extensions.

Am still thinking about it though.

I assume the result is 0fe1ef24f7bd0020f29ffe287dfdb9ead33ca0b2?

yup.

When I saw this commit I wondered that it was you who told me to think about this but the commiter was Linus.

A long story. I rubbed a magic lantern ;)

Quoted 7 lines
Quoted 6 lines
Quoted 2 lines

Should I send a new version using %pF or is it easier if you fix up the patch?

A new version would be nice, thanks. Or an incremental diff, but I can and will turn new versions into an incremental in the twinkle of an eye, so either is OK.

voila:

Thanks.

A message that is not in the archive (sent off-list or not yet received): [email protected]

A message that is not in the archive (sent off-list or not yet received): [email protected]

#14 lore raw

Re: the printk problem

Quoted 7 lines · Linus Torvalds

so I think we could easily just say that we extend %p in various ways:

- %pS - print pointer as a symbol

and leave tons of room for future extensions for different kinds of pointers.

So here's a totally untested example patch of this, which could probably easily be extended to to other things.

I actually made it '%pF' and '%pS' for a Function descriptor pointer and normal Symbolic pointer respectively, because of the stupid things ia64 and PPC64 do with the pointer indirection through function descriptors.

That function descriptor indirection is totally untested, and I did it with a

	pagefault_disable();
	__get_user(..)
	pagefault_enable();

thing because I thought it would be nice if printk() was always safe, and passing bogus function pointers to '%pF' should try to work, but quite frankly, I didn't even check that that part compiles, much less works.

ia64/ppc lists cc'd, just in case somebody wants to test this.

NOTE! There are no current actual users of this, but the _idea_ is that we should be able to just do

	printk("%pF\n", desc->handle_irq);

instead of using

	print_symbol("%s\n", (unsigned long)desc->handle_irq);

The latter is from kernel/irq/internals.h, and actually looks incorrect - shouldn't it use print_fn_descriptor_symbol(), since it's a C level function pointer? We should use "print_symbol()" for return pointers we find on the stack and data pointers, but not for stuff that actually has a C type that is a function pointer?

Somebody who cares about the insane function descriptors should take a deeper look.

NOTE AGAIN! UNTESTED! I could easily have screwed up printk() _entirely_, since I had to factor out the string handling into a function of its own.

Linus

---
 lib/vsprintf.c |  102 ++++++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 74 insertions(+), 28 deletions(-)
lib/vsprintf.c +74 -28
index 6021757..148b656 100644
@@ -22,6 +22,7 @@
 #include <linux/string.h>
 #include <linux/ctype.h>
 #include <linux/kernel.h>
+#include <linux/kallsyms.h>
 
 #include <asm/page.h>		/* for PAGE_SIZE */
 #include <asm/div64.h>
@@ -482,6 +483,72 @@ static char *number(char *buf, char *end, unsigned long long num, int base, int
 	return buf;
 }
 
+static char *string(char *buf, char *end, char *s, int field_width, int precision, int flags)
+{
+	int len, i;
+
+	if ((unsigned long)s < PAGE_SIZE)
+		s = "<NULL>";
+
+	len = strnlen(s, precision);
+
+	if (!(flags & LEFT)) {
+		while (len < field_width--) {
+			if (buf < end)
+				*buf = ' ';
+			++buf;
+		}
+	}
+	for (i = 0; i < len; ++i) {
+		if (buf < end)
+			*buf = *s;
+		++buf; ++s;
+	}
+	while (len < field_width--) {
+		if (buf < end)
+			*buf = ' ';
+		++buf;
+	}
+	return buf;
+}
+
+/*
+ * Show a '%p' thing.  A kernel extension is that the '%p' is followed
+ * by an extra set of alphanumeric characters that are extended format
+ * specifiers.  Right now we just handle 'F' (for symbolic Function
+ * pointers) and 'S' (for Symbolic data pointers), but this can easily
+ * be extended in the future (network address types etc).
+ *
+ * The difference between 'S' and 'F' is that on ia64 and ppc64 function
+ * pointers are really function descriptors, which contain a pointer the
+ * real address. 
+ */
+static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int base, int size, int precision, int type)
+{
+	char sym[KSYM_SYMBOL_LEN];
+	switch (*fmt) {
+	case 'F':	/* Function pointer */
+#if defined(CONFIG_IA64) || defined(CONFIG_PPC64)
+		{ void *p;
+		pagefault_disable();
+		if (!__get_user((void **)ptr, &p))
+			ptr = p;
+		pagefault_enable();
+		}
+#endif
+	/* Fallthrough */
+	case 'S':	/* Other pointer */
+#if CONFIG_KALLSYMS
+		sprint_symbol(sym, (unsigned long) ptr);
+		return string(buf, end, sym, size, precision, type);
+#else
+		type |= SPECIAL;
+		break;
+#endif
+	}
+	return number(buf, end, (unsigned long long) ptr, base, size, precision, type);
+}
+
 /**
  * vsnprintf - Format a string and place it in a buffer
  * @buf: The buffer to place the result into
@@ -502,11 +569,9 @@ static char *number(char *buf, char *end, unsigned long long num, int base, int
  */
 int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 {
-	int len;
 	unsigned long long num;
-	int i, base;
+	int base;
 	char *str, *end, c;
-	const char *s;
 
 	int flags;		/* flags to number() */
 
@@ -622,29 +687,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 				continue;
 
 			case 's':
-				s = va_arg(args, char *);
-				if ((unsigned long)s < PAGE_SIZE)
-					s = "<NULL>";
-
-				len = strnlen(s, precision);
-
-				if (!(flags & LEFT)) {
-					while (len < field_width--) {
-						if (str < end)
-							*str = ' ';
-						++str;
-					}
-				}
-				for (i = 0; i < len; ++i) {
-					if (str < end)
-						*str = *s;
-					++str; ++s;
-				}
-				while (len < field_width--) {
-					if (str < end)
-						*str = ' ';
-					++str;
-				}
+				str = string(str, end, va_arg(args, char *), field_width, precision, flags);
 				continue;
 
 			case 'p':
@@ -653,9 +696,12 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 					field_width = 2*sizeof(void *);
 					flags |= ZEROPAD;
 				}
-				str = number(str, end,
-						(unsigned long) va_arg(args, void *),
+				str = pointer(fmt+1, str, end,
+						va_arg(args, void *),
 						16, field_width, precision, flags);
+				/* Skip all alphanumeric pointer suffixes */
+				while (isalnum(fmt[1]))
+					fmt++;
 				continue;
 
 
maintainer
#15 lore raw

Re: the printk problem

Quoted 21 lines · On Fri, 4 Jul 2008 13:02:05 -0700 (PDT) Linus Torvalds…
Quoted 7 lines · Linus Torvalds

so I think we could easily just say that we extend %p in various ways:

- %pS - print pointer as a symbol

and leave tons of room for future extensions for different kinds of pointers.

So here's a totally untested example patch of this, which could probably easily be extended to to other things.

I actually made it '%pF' and '%pS' for a Function descriptor pointer and normal Symbolic pointer respectively, because of the stupid things ia64 and PPC64 do with the pointer indirection through function descriptors.

That function descriptor indirection is totally untested, and I did it with a

Quoted 7 lines
	pagefault_disable();
	__get_user(..)
	pagefault_enable();

thing because I thought it would be nice if printk() was always safe, and passing bogus function pointers to '%pF' should try to work, but quite frankly, I didn't even check that that part compiles, much less works.

probe_kernel_address() should be usable here.

Quoted 41 lines

ia64/ppc lists cc'd, just in case somebody wants to test this.

NOTE! There are no current actual users of this, but the _idea_ is that we should be able to just do

	printk("%pF\n", desc->handle_irq);

instead of using

	print_symbol("%s\n", (unsigned long)desc->handle_irq);

The latter is from kernel/irq/internals.h, and actually looks incorrect - shouldn't it use print_fn_descriptor_symbol(), since it's a C level function pointer? We should use "print_symbol()" for return pointers we find on the stack and data pointers, but not for stuff that actually has a C type that is a function pointer?

Somebody who cares about the insane function descriptors should take a deeper look.

NOTE AGAIN! UNTESTED! I could easily have screwed up printk() _entirely_, since I had to factor out the string handling into a function of its own.

Linus

---
 lib/vsprintf.c |  102 ++++++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 74 insertions(+), 28 deletions(-)
lib/vsprintf.c +1 -0
index 6021757..148b656 100644
@@ -22,6 +22,7 @@
 #include <linux/string.h>
 #include <linux/ctype.h>
 #include <linux/kernel.h>
+#include <linux/kallsyms.h>
 
 #include <asm/page.h>		/* for PAGE_SIZE */
 #include <asm/div64.h>

lib/vsprintf.c static char *number(char *buf, char *end, unsigned long long num, int base, int · line 491

Quoted 9 lines
lib/vsprintf.c +6 -0
@@ -482,6 +483,72 @@ static char *number(char *buf, char *end, unsigned long long num, int base, int
 	return buf;
 }
 
+static char *string(char *buf, char *end, char *s, int field_width, int precision, int flags)
+{
+	int len, i;
+
+	if ((unsigned long)s < PAGE_SIZE)
+		s = "<NULL>";

hm, is that needed for other reasons than "it will fault"?

otherwise we could walk the whole string with probe_kernel_address() before we do anything with it.

That's slightly racy against vunmap and CONFIG_DEBUG_PAGEALLOC and stuff. Fixable by *never* dereferencing that pointer except via probe_kernel_address().

Quoted 48 lines
lib/vsprintf.c +48 -0
+	len = strnlen(s, precision);
+
+	if (!(flags & LEFT)) {
+		while (len < field_width--) {
+			if (buf < end)
+				*buf = ' ';
+			++buf;
+		}
+	}
+	for (i = 0; i < len; ++i) {
+		if (buf < end)
+			*buf = *s;
+		++buf; ++s;
+	}
+	while (len < field_width--) {
+		if (buf < end)
+			*buf = ' ';
+		++buf;
+	}
+	return buf;
+}
+
+/*
+ * Show a '%p' thing.  A kernel extension is that the '%p' is followed
+ * by an extra set of alphanumeric characters that are extended format
+ * specifiers.  Right now we just handle 'F' (for symbolic Function
+ * pointers) and 'S' (for Symbolic data pointers), but this can easily
+ * be extended in the future (network address types etc).
+ *
+ * The difference between 'S' and 'F' is that on ia64 and ppc64 function
+ * pointers are really function descriptors, which contain a pointer the
+ * real address. 
+ */
+static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int base, int size, int precision, int type)
+{
+	char sym[KSYM_SYMBOL_LEN];
+	switch (*fmt) {
+	case 'F':	/* Function pointer */
+#if defined(CONFIG_IA64) || defined(CONFIG_PPC64)
+		{ void *p;
+		pagefault_disable();
+		if (!__get_user((void **)ptr, &p))
+			ptr = p;
+		pagefault_enable();
+		}
+#endif
+	/* Fallthrough */
+	case 'S':	/* Other pointer */

lib/vsprintf.c static char *number(char *buf, char *end, unsigned long long num, int base, int · line 550

Quoted 10 lines
lib/vsprintf.c +10 -0
+#if CONFIG_KALLSYMS
+		sprint_symbol(sym, (unsigned long) ptr);
+		return string(buf, end, sym, size, precision, type);
+#else
+		type |= SPECIAL;
+		break;
+#endif
+	}
+	return number(buf, end, (unsigned long long) ptr, base, size, precision, type);
+}

If this takes off we might want a register-your-printk-handler interface. Maybe.

We also jump through hoops to print things like sector_t and resource_size_t. They always need to be cast to `unsiged long long', which generates additional stack space and text in some setups.

And then there's the perennial "need to cast u64 to unsigned long long to print it". If we were to do

	printk("%SL", (void *)some_u64);

then that's still bloody ugly, but it'll save a little text-n-stack.

#16 lore raw

Re: the printk problem

Quoted 2 lines · Andrew Morton

probe_kernel_address() should be usable here.

Right you are.

Quoted 8 lines

lib/vsprintf.c static char *number(char *buf, char *end, unsigned long long num, int base, int · line 491

Quoted 6 lines
lib/vsprintf.c +6 -0
+static char *string(char *buf, char *end, char *s, int field_width, int precision, int flags)
+{
+	int len, i;
+
+	if ((unsigned long)s < PAGE_SIZE)
+		s = "<NULL>";

hm, is that needed for other reasons than "it will fault"?

It's similar to what glibc does - showing a NULL ptr gracefully. They are pretty common, and it's very rude to SIGSEGV or oops just because you wanted to print a string out for debugging.

The code is also just copied from the old code - just moved it to a function of its own.

Quoted 2 lines

otherwise we could walk the whole string with probe_kernel_address() before we do anything with it.

That would be pretty slow, wed' be better off then unrolling it (ie doing all the ugly setup around the whole string).

Quoted 2 lines

If this takes off we might want a register-your-printk-handler interface. Maybe.

Yeah.

Quoted 3 lines

We also jump through hoops to print things like sector_t and resource_size_t. They always need to be cast to `unsiged long long', which generates additional stack space and text in some setups.

Indeed. Though doing it with a pointer is often not a _whole_ lot cleaner, but yes, it's often nicer to add a '&' than adding a cast.

Quoted 6 lines

And then there's the perennial "need to cast u64 to unsigned long long to print it". If we were to do

	printk("%SL", (void *)some_u64);

then that's still bloody ugly, but it'll save a little text-n-stack.

No can do. (void *) isn't big enough to hold a u64. So it would have to be something like this:

	printk("%p64i", &some_u64);

instead. Avoiding the cast, and often being more efficient calling convention on 32-bit.

But it can often generate worse code too - now we're taking an address of a variable, and that will disable many optimizations because now i's not a purely local variable that the compiler knows all uses for any more.

So I'm not entirely conviced it's a good idea to do this for just "long long" cases. Dunno.

Linus

#17 lore raw

Re: the printk problem

In reply to Andrew Morton
Quoted 8 lines · Andrew Morton
Quoted 6 lines · On Fri, 4 Jul 2008 13:02:05 -0700 (PDT) Linus Torvalds…
Quoted 6 lines

so I think we could easily just say that we extend %p in various ways:

- %pS - print pointer as a symbol

and leave tons of room for future extensions for different kinds of pointers.

Quoted 6 lines

If this takes off we might want a register-your-printk-handler interface. Maybe.

We also jump through hoops to print things like sector_t and resource_size_t. They always need to be cast to `unsiged long long', which generates additional stack space and text in some setups.

The thing is that GCC checks types. So it's fine to add "print this pointer specially", but you can't in general add new printf arguments without also hacking GCC. Unless you use -Wno-format, and require sparse to check special kernel types.

Quoted 6 lines

And then there's the perennial "need to cast u64 to unsigned long long to print it". If we were to do

	printk("%SL", (void *)some_u64);

then that's still bloody ugly, but it'll save a little text-n-stack.

u64 is easy to fix, and I don't know why we haven't. Just make it unsigned long long on all architectures.

Signature · 4 lines
Intel are signing my paycheques ... these opinions are still mine
"Bill, look, we understand that you're interested in selling us this
operating system, but compare it to ours.  We can't possibly take such
a retrograde step."
maintainer
#18 lore raw

Re: the printk problem

(heck, let's cc lkml - avoid having to go through all this again)

Quoted 12 lines · On Fri, 4 Jul 2008 14:42:53 -0600 Matthew Wilcox <[email protected]> wrote:
Quoted 11 lines · Andrew Morton
Quoted 6 lines · On Fri, 4 Jul 2008 13:02:05 -0700 (PDT) Linus Torvalds…
Quoted 6 lines

so I think we could easily just say that we extend %p in various ways:

- %pS - print pointer as a symbol

and leave tons of room for future extensions for different kinds of pointers.

If this takes off we might want a register-your-printk-handler interface. Maybe.

Quoted 8 lines
Quoted 3 lines

We also jump through hoops to print things like sector_t and resource_size_t. They always need to be cast to `unsiged long long', which generates additional stack space and text in some setups.

The thing is that GCC checks types. So it's fine to add "print this pointer specially", but you can't in general add new printf arguments without also hacking GCC. Unless you use -Wno-format, and require sparse to check special kernel types.

It would be excellent if gcc had an extension system so that you could add new printf control chars and maybe even tell gcc how to check them. But of course, if that were to happen, we couldn't use it for 4-5 years.

What I had initially proposed was to abuse %S, which takes a wchar_t*. gcc accepts `unsigned long *' for %S.

Then, we put the kernel-specific control char after the S, so we can print an inode (rofl) with

	struct inode *inode;

	printk("here is an inode: %Si\n", (unsigned long *)inode);

Downsides are:

- there's a cast, so you could accidentally do

	printk("here is an inode: %Si\n", (unsigned long *)dentry);

- there's a cast, and they're ugly

- gcc cannot of course check that the arg matches the control string

Unfortunately (and this seems weird), gcc printf checking will not accept a void* for %S: it _has_ to be wchar_t*, and the checker won't permit void* substitution for that.

Anyway, Linus took all that and said "let's abuse %p instead". It _will_ accept any pointer so we can instead do:

	printk("here is an inode: %pi\n", inode);

which is nicer.

I think the main customers of this are print_symbol():

	printk("I am about to call %ps\n", fn);
	(*fn)();

and NIPQUAD and its ipv6 version.

We don't know how much interest there would be in churning NIPQUAD from the net guys. Interestingly, there's also %C (wint_t) which is a 32-bit quantity. So we could just go and say "%C prints an ipv4 address" and be done with it. But there's no way of doing that for ipv6 addresses so things would become asymmetrical there.

Another customer is net mac addresses. There are surely others. One which should have been in printf 30 years ago was %b: binary.

Quoted 9 lines
Quoted 6 lines

And then there's the perennial "need to cast u64 to unsigned long long to print it". If we were to do

	printk("%SL", (void *)some_u64);

then that's still bloody ugly, but it'll save a little text-n-stack.

u64 is easy to fix, and I don't know why we haven't. Just make it unsigned long long on all architectures.

Yeah. Why don't we do that?

#19 lore raw

Re: the printk problem

Quoted 5 lines · Andrew Morton

(heck, let's cc lkml - avoid having to go through all this again)

It would be excellent if gcc had an extension system so that you could add new printf control chars and maybe even tell gcc how to check them. But of course, if that were to happen, we couldn't use it for 4-5 years.

I believe NetBSD added that as an extension many years ago. Dunno if they still have it.

Quoted 3 lines

What I had initially proposed was to abuse %S, which takes a wchar_t*. gcc accepts `unsigned long *' for %S.

[...]

Quoted 11 lines

- there's a cast, so you could accidentally do

	printk("here is an inode: %Si\n", (unsigned long *)dentry);

- there's a cast, and they're ugly

- gcc cannot of course check that the arg matches the control string

Unfortunately (and this seems weird), gcc printf checking will not accept a void* for %S: it _has_ to be wchar_t*, and the checker won't permit void* substitution for that.

Presumably that's the compiler getting rid of the first and third downside ;-)

Quoted 6 lines

Anyway, Linus took all that and said "let's abuse %p instead". It _will_ accept any pointer so we can instead do:

	printk("here is an inode: %pi\n", inode);

which is nicer.

Yes. It's possible to confuse it, of course.

	printk("Function %pSucks\n", sys_open);

but I really doubt we have such a usage in the kernel today.

Quoted 4 lines
Quoted 2 lines

u64 is easy to fix, and I don't know why we haven't. Just make it unsigned long long on all architectures.

Yeah. Why don't we do that?

Patch ...

[PATCH] Make u64 long long on all architectures

It is currently awkward to print a u64 type. Some architectures use unsigned long while others use unsigned long long. Since unsigned long long is 64-bit for all existing Linux architectures, change those that use long to use long long. Note that this applies only within the kernel. If u64 is being used in a C++ method definition, the symbol mangling would change.

include/asm-generic/int-l64.h +5 -0
index 2af9b75..32f07bd 100644
@@ -23,8 +23,13 @@ typedef unsigned short __u16;
 typedef __signed__ int __s32;
 typedef unsigned int __u32;
 
+#ifdef __KERNEL__
+typedef __signed__ long long __s64;
+typedef unsigned long long __u64;
+#else
 typedef __signed__ long __s64;
 typedef unsigned long __u64;
+#endif
 
 #endif /* __ASSEMBLY__ */
 
Signature · 4 lines
Intel are signing my paycheques ... these opinions are still mine
"Bill, look, we understand that you're interested in selling us this
operating system, but compare it to ours.  We can't possibly take such
a retrograde step."
maintainer
#20 lore raw

Re: [PATCH] Make u64 long long on all architectures (was: the printk problem)

Quoted 18 lines · On Fri, 4 Jul 2008 20:03:51 -0600 Matthew Wilcox <[email protected]> wrote:

[PATCH] Make u64 long long on all architectures

It is currently awkward to print a u64 type. Some architectures use unsigned long while others use unsigned long long. Since unsigned long long is 64-bit for all existing Linux architectures, change those that use long to use long long. Note that this applies only within the kernel. If u64 is being used in a C++ method definition, the symbol mangling would change.

include/asm-generic/int-l64.h +0 -0
index 2af9b75..32f07bd 100644
@@ -23,8 +23,13 @@ typedef unsigned short __u16;
 typedef __signed__ int __s32;
 typedef unsigned int __u32;

include/asm-generic/int-l64.h typedef unsigned short __u16; · line 34

Quoted 10 lines
include/asm-generic/int-l64.h +5 -0
 
+#ifdef __KERNEL__
+typedef __signed__ long long __s64;
+typedef unsigned long long __u64;
+#else
 typedef __signed__ long __s64;
 typedef unsigned long __u64;
+#endif
 
 #endif /* __ASSEMBLY__ */

This is (IMO) a desirable change and will prevent a heck of a lot of goofing around, and will permit a lot of prior goofing around to be removed.

But I bet there are lots of instalces of printk("%l", some_u64) down in arch code where the type of u64 _is_ known which will now spew warnings.

Oh well.

#21 lore raw

Re: [PATCH] Make u64 long long on all architectures (was: the printk problem)

Quoted 35 lines · Andrew Morton
Quoted 18 lines · On Fri, 4 Jul 2008 20:03:51 -0600 Matthew Wilcox <[email protected]> wrote:

[PATCH] Make u64 long long on all architectures

It is currently awkward to print a u64 type. Some architectures use unsigned long while others use unsigned long long. Since unsigned long long is 64-bit for all existing Linux architectures, change those that use long to use long long. Note that this applies only within the kernel. If u64 is being used in a C++ method definition, the symbol mangling would change.

include/asm-generic/int-l64.h +0 -0
index 2af9b75..32f07bd 100644
@@ -23,8 +23,13 @@ typedef unsigned short __u16;
 typedef __signed__ int __s32;
 typedef unsigned int __u32;

include/asm-generic/int-l64.h typedef unsigned short __u16; · line 34

Quoted 10 lines
include/asm-generic/int-l64.h +5 -0
 
+#ifdef __KERNEL__
+typedef __signed__ long long __s64;
+typedef unsigned long long __u64;
+#else
 typedef __signed__ long __s64;
 typedef unsigned long __u64;
+#endif
 
 #endif /* __ASSEMBLY__ */

This is (IMO) a desirable change and will prevent a heck of a lot of goofing around, and will permit a lot of prior goofing around to be removed.

Quoted 4 lines

But I bet there are lots of instalces of printk("%l", some_u64) down in arch code where the type of u64 _is_ known which will now spew warnings.

Oh well.

As a rough estimate:

concordia powerpc(master) $ find arch/powerpc/ ! -name '*32.*' | xargs grep "%l" | grep -v "%ll" | wc -l 635

Someone's gonna get a lot of git points for fixing all those. Might keep the speeling fix crowd busy for a while.

cheers

Signature · 8 lines
Michael Ellerman
OzLabs, IBM Australia Development Lab

wwweb: http://michael.ellerman.id.au
phone: +61 2 6212 1183 (tie line 70 21183)

We do not inherit the earth from our ancestors,
we borrow it from our children. - S.M.A.R.T Person
maintainer
#22 lore raw

Re: [PATCH] Make u64 long long on all architectures (was: the printk problem)

Quoted 39 lines · On Tue, 22 Jul 2008 20:36:35 +1000 Michael Ellerman <[email protected]>…
Quoted 38 lines · Andrew Morton
Quoted 18 lines · On Fri, 4 Jul 2008 20:03:51 -0600 Matthew Wilcox <[email protected]> wrote:

[PATCH] Make u64 long long on all architectures

It is currently awkward to print a u64 type. Some architectures use unsigned long while others use unsigned long long. Since unsigned long long is 64-bit for all existing Linux architectures, change those that use long to use long long. Note that this applies only within the kernel. If u64 is being used in a C++ method definition, the symbol mangling would change.

include/asm-generic/int-l64.h +0 -0
index 2af9b75..32f07bd 100644
@@ -23,8 +23,13 @@ typedef unsigned short __u16;
 typedef __signed__ int __s32;
 typedef unsigned int __u32;

include/asm-generic/int-l64.h typedef unsigned short __u16; · line 34

Quoted 10 lines
include/asm-generic/int-l64.h +5 -0
 
+#ifdef __KERNEL__
+typedef __signed__ long long __s64;
+typedef unsigned long long __u64;
+#else
 typedef __signed__ long __s64;
 typedef unsigned long __u64;
+#endif
 
 #endif /* __ASSEMBLY__ */

This is (IMO) a desirable change and will prevent a heck of a lot of goofing around, and will permit a lot of prior goofing around to be removed.

But I bet there are lots of instalces of printk("%l", some_u64) down in arch code where the type of u64 _is_ known which will now spew warnings.

Quoted 6 lines
Quoted 1 line

Oh well.

As a rough estimate:

concordia powerpc(master) $ find arch/powerpc/ ! -name '*32.*' | xargs grep "%l" | grep -v "%ll" | wc -l 635

lolz. If yesterdays-linux-next on todays-mainline wasn't such a hilarious trainwreck I'd test your grepping. I guess that could be done on mainline too.

Quoted 2 lines

Someone's gonna get a lot of git points for fixing all those. Might keep the speeling fix crowd busy for a while.

I'm not sure I have the energy for this.

But we really should do it.

#23 lore raw

Re: [PATCH] Make u64 long long on all architectures (was: the printk problem)

In reply to Michael Ellerman
Quoted 8 lines · Michael Ellerman

concordia powerpc(master) $ find arch/powerpc/ ! -name '*32.*' | xargs grep "%l" | grep -v "%ll" | wc -l
635

Someone's gonna get a lot of git points for fixing all those. Might keep
the speeling fix crowd busy for a

But a bunch of those might actually be real longs, not u64's ...

Easier to do the change and build something like ppc6xx_defconfig to get a better approximation.

Ben.

#24 lore raw

Re: [PATCH] Make u64 long long on all architectures (was: the printk problem)

In reply to Andrew Morton
Quoted 8 lines

This is (IMO) a desirable change and will prevent a heck of a lot of goofing around, and will permit a lot of prior goofing around to be removed.

But I bet there are lots of instalces of printk("%l", some_u64) down in arch code where the type of u64 _is_ known which will now spew warnings.

Well, I'm about to call a big "warning fixing day" on the powerpc list, I saw a few today when building a couple of configs and that hurts my eyes so we may as well fold that in :-)

Cheers,
Ben.

#25 lore raw

Re: the printk problem

In reply to Andrew Morton
Quoted 29 lines · Andrew Morton
Quoted 8 lines
Quoted 3 lines

We also jump through hoops to print things like sector_t and resource_size_t. They always need to be cast to `unsiged long long', which generates additional stack space and text in some setups.

The thing is that GCC checks types. So it's fine to add "print this pointer specially", but you can't in general add new printf arguments without also hacking GCC. Unless you use -Wno-format, and require sparse to check special kernel types.

It would be excellent if gcc had an extension system so that you could add new printf control chars and maybe even tell gcc how to check them. But of course, if that were to happen, we couldn't use it for 4-5 years.

What I had initially proposed was to abuse %S, which takes a wchar_t*. gcc accepts `unsigned long *' for %S.

Then, we put the kernel-specific control char after the S, so we can print an inode (rofl) with

	struct inode *inode;

	printk("here is an inode: %Si\n", (unsigned long *)inode);

Downsides are:

- there's a cast, so you could accidentally do

	printk("here is an inode: %Si\n", (unsigned long *)dentry);
Quoted 7 lines

- there's a cast, and they're ugly

- gcc cannot of course check that the arg matches the control string

Unfortunately (and this seems weird), gcc printf checking will not accept a void* for %S: it _has_ to be wchar_t*, and the checker won't permit void* substitution for that.

Repeating myself here...
We can add an alternative alias to printk:

asmlinkage int printk(const char * fmt, ...)

         __attribute__ ((format (printf, 1, 2))) __cold;

+asmlinkage int custom_printk(const char * fmt, ...) __cold asm ("printk");

custom_printk() is actually just printk(), that is, we won't need additional function, we need to teach *printk* about MAC addresses, NIPQUADs etc;

and then use printk() if you use only standard %fmt (and have it checked by gcc), or use custom_printk() if you have non-standard %fmt in the format string.

The only downside that in second case, you lose gcc checking. No big deal.
--
vda

#26 lore raw

Re: the printk problem

In reply to Andrew Morton
Quoted 6 lines · Andrew Morton

We don't know how much interest there would be in churning NIPQUAD from the net guys. Interestingly, there's also %C (wint_t) which is a 32-bit quantity. So we could just go and say "%C prints an ipv4 address" and be done with it. But there's no way of doing that for ipv6 addresses so things would become asymmetrical there.

	struct in6_addr src;
	printk("Source address: %p{ipv6}\n", &src);

How about %p{feature}?

#27 lore raw

Re: the printk problem

Quoted 9 lines · Jan Engelhardt
Quoted 6 lines · Andrew Morton

We don't know how much interest there would be in churning NIPQUAD from the net guys. Interestingly, there's also %C (wint_t) which is a 32-bit quantity. So we could just go and say "%C prints an ipv4 address" and be done with it. But there's no way of doing that for ipv6 addresses so things would become asymmetrical there.

Quoted 4 lines
       struct in6_addr src;
       printk("Source address: %p{ipv6}\n", &src);

How about %p{feature}?

Something like this?

(It's hard on the stack, yes, I know. We should fix kallsyms.)

Vegard

From: Vegard Nossum <[email protected]>
Date: Sat, 5 Jul 2008 14:01:00 +0200
Subject: [PATCH] printf: add %p{} extension

---
 lib/vsprintf.c |   44 ++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 44 insertions(+), 0 deletions(-)
lib/vsprintf.c +44 -0
index 6021757..011cf3f 100644
@@ -17,6 +17,7 @@
  */
 
 #include <stdarg.h>
+#include <linux/kallsyms.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/string.h>
@@ -366,6 +367,30 @@ static noinline char* put_dec(char *buf, unsigned long long num)
 #define SMALL	32		/* Must be 32 = 0x20 */
 #define SPECIAL	64		/* 0x */
 
+static char *custom_format(char *buf, char *end,
+	const char *fmt, unsigned int fmtlen, void *arg)
+{
+	if (!strncmp(fmt, "sym", fmtlen)) {
+		char name[KSYM_SYMBOL_LEN];
+		int len;
+		int i;
+
+		len = sprint_symbol(name, (unsigned long) arg);
+		if (len < 0)
+			return buf;
+
+		i = 0;
+		while (i < len) {
+			if (buf < end)
+				*buf = name[i];
+			++buf;
+			++i;
+		}
+	}
+
+	return buf;
+}
+
 static char *number(char *buf, char *end, unsigned long long num, int base, int size, int precision, int type)
 {
 	/* we are called with base 8, 10 or 16, only, thus don't need "G..."  */
@@ -648,6 +673,25 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 				continue;
 
 			case 'p':
+				if (fmt[1] = '{') {
+					const char *cfmt;
+
+					/* Skip the '%{' */
+					++fmt;
+					++fmt;
+
+					cfmt = fmt;
+
+					/* Skip everything up to the '}' */
+					while (*fmt && *fmt != '}')
+						++fmt;
+
+					str = custom_format(str, end,
+						cfmt, fmt - cfmt,
+						va_arg(args, void *));
+					continue;
+				}
+
 				flags |= SMALL;
 				if (field_width = -1) {
 					field_width = 2*sizeof(void *);
Signature · 1 line
1.5.4.1
#28 lore raw

Re: the printk problem

Quoted 15 lines · Vegard Nossum
Quoted 8 lines · Andrew Morton
Quoted 6 lines

We don't know how much interest there would be in churning NIPQUAD from the net guys. Interestingly, there's also %C (wint_t) which is a 32-bit quantity. So we could just go and say "%C prints an ipv4 address" and be done with it. But there's no way of doing that for ipv6 addresses so things would become asymmetrical there.

Quoted 4 lines
       struct in6_addr src;
       printk("Source address: %p{ipv6}\n", &src);

How about %p{feature}?

Something like this?

lib/vsprintf.c static noinline char* put_dec(char *buf, unsigned long long num) · line 371

Quoted 2 lines
lib/vsprintf.c +2 -0
+static char *custom_format(char *buf, char *end,
+	const char *fmt, unsigned int fmtlen, void *arg)

I'd use const void *arg here, so nobody gets the idea that you could modify the argument while printing :)

Quoted 8 lines
lib/vsprintf.c +8 -0
+{
+	if (!strncmp(fmt, "sym", fmtlen)) {
+		char name[KSYM_SYMBOL_LEN];
+		int len;
+		int i;
+
+		len = sprint_symbol(name, (unsigned long) arg);
+		if (len < 0)

lib/vsprintf.c static noinline char* put_dec(char *buf, unsigned long long num) · line 389

Quoted 10 lines
lib/vsprintf.c +10 -0
+			return buf;
+
+		i = 0;
+		while (i < len) {
+			if (buf < end)
+				*buf = name[i];
+			++buf;
+			++i;
+		}
+	}

And I assume it's then as simple as

	} else if (strncmp(fmt, "nip6", fmtlen) = 0) {
		snprintf(buf, end - buf (+1?), NIP6_FMT in expanded form,
		         NIP6 in expanded form(arg));
	}

Hm, that's going to get a big function when everyone adds their fmttypes.

Quoted 8 lines
lib/vsprintf.c +4 -0
+
+	return buf;
+}
+

static char *number(char *buf, char *end, unsigned long long num, int base, int size, int precision, int type)

{
	/* we are called with base 8, 10 or 16, only, thus don't need "G..."  */

lib/vsprintf.c int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) · line 682

Quoted 10 lines
lib/vsprintf.c +7 -0
@@ -648,6 +673,25 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 				continue;
 
 			case 'p':
+				if (fmt[1] = '{') {
+					const char *cfmt;
+
+					/* Skip the '%{' */
+					++fmt;
+					++fmt;
+

Not this?

	/* Skip the '%p{' */
	fmt += 3;
#29 lore raw

Re: the printk problem

Quoted 15 lines · Jan Engelhardt
Quoted 13 lines · Vegard Nossum
Quoted 12 lines · Andrew Morton
Quoted 6 lines

We don't know how much interest there would be in churning NIPQUAD from the net guys. Interestingly, there's also %C (wint_t) which is a 32-bit quantity. So we could just go and say "%C prints an ipv4 address" and be done with it. But there's no way of doing that for ipv6 addresses so things would become asymmetrical there.

       struct in6_addr src;
       printk("Source address: %p{ipv6}\n", &src);

How about %p{feature}?

Quoted 8 lines

lib/vsprintf.c static noinline char* put_dec(char *buf, unsigned long long num) · line 371

Quoted 4 lines

Something like this?

lib/vsprintf.c +2 -0
+static char *custom_format(char *buf, char *end,
+      const char *fmt, unsigned int fmtlen, void *arg)

I'd use const void *arg here, so nobody gets the idea that you could modify the argument while printing :)

Oops, of course. Thanks.

Quoted 2 lines
Quoted 2 lines
lib/vsprintf.c +2 -0
+{
+      if (!strncmp(fmt, "sym", fmtlen)) {

...

Quoted 4 lines
Quoted 1 line

+ }

And I assume it's then as simple as

Quoted 8 lines
       } else if (strncmp(fmt, "nip6", fmtlen) = 0) {
               snprintf(buf, end - buf (+1?), NIP6_FMT in expanded form,
                        NIP6 in expanded form(arg));
       }

Hm, that's going to get a big function when everyone adds their fmttypes.

Yes. Luckily, the entry point is then fixed in a single place and it's easy to convert it into something more dynamic :-)

A static array wouldn't help much because it still concentrates all the names. But we could at least do a binary search on that and get some speed improvements if it grows large.

I think the most elegant solution would be a macro similar to the initcall macros, that adds the custom extensions to a table which is defined by a special linker section. This allows complete decentralization, but I don't think it's possible to do binary search on the names anymore.

Dynamic registering/unregistering could be done too. Maybe this is a good thing for modules...

Thoughts?

Quoted 21 lines
Quoted 8 lines
lib/vsprintf.c +4 -0
+
+      return buf;
+}
+

static char *number(char *buf, char *end, unsigned long long num, int base, int size, int precision, int type)

{
      /* we are called with base 8, 10 or 16, only, thus don't need "G..."  */

lib/vsprintf.c int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) · line 682

Quoted 10 lines
lib/vsprintf.c +7 -0
@@ -648,6 +673,25 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
                              continue;
 
                      case 'p':
+                              if (fmt[1] = '{') {
+                                      const char *cfmt;
+
+                                      /* Skip the '%{' */
+                                      ++fmt;
+                                      ++fmt;
+

Not this?

Quoted 3 lines
       /* Skip the '%p{' */
       fmt += 3;

Oops, the comment is wrong. It should say: "Skip the p{". But fmt += 3 is wrong. Since fmt[0] is at this point 'p', and fmt[1] is '{'. The % and flags, etc. have already been skipped by the common code. So it should be fmt += 2 :-)

Thanks!

Vegard

Signature · 4 lines
"The animistic metaphor of the bug that maliciously sneaked in while
the programmer was not looking is intellectually dishonest as it
disguises that the error is the programmer's own creation."
	-- E. W. Dijkstra, EWD1036
#30 lore raw

Re: the printk problem

Quoted 6 lines · Vegard Nossum

I think the most elegant solution would be a macro similar to the initcall macros, that adds the custom extensions to a table which is defined by a special linker section. This allows complete decentralization, but I don't think it's possible to do binary search on the names anymore.

With an rbtree, you can still do binary search.

#31 lore raw

Re: the printk problem

In reply to Vegard Nossum
Quoted 10 lines · Vegard Nossum
Quoted 9 lines · Jan Engelhardt
Quoted 6 lines · Andrew Morton

We don't know how much interest there would be in churning NIPQUAD from the net guys. Interestingly, there's also %C (wint_t) which is a 32-bit quantity. So we could just go and say "%C prints an ipv4 address" and be done with it. But there's no way of doing that for ipv6 addresses so things would become asymmetrical there.

Quoted 4 lines
Quoted 4 lines
       struct in6_addr src;
       printk("Source address: %p{ipv6}\n", &src);

How about %p{feature}?

No.

I _expressly_ chose '%p[alphanumeric]*' because it's basically totally insane to have that in a *real* printk() string: the end result would be totally unreadable.

In contrast, '%p[specialchar]' is not unreadable, and in fact we have lots of those already in the kernel. In fact, there are 40 occurrences of '%p{' in the kernel, just grep for it (especially the AFS code seems to be very happy to use that kind of printout in its debug statements).

So it makes perfect sense to have a _real_ printk string that says

	"BUG: Dentry %p{i=%lx,n=%s}"

where we have that '%p{...' sequence: the end result is easily parseable. In contrast, anybody who uses '%pS' or something like that and expects a pointer name to be immediately followed by teh letter 'S' is simply insane, because the end result is an unreadable mess.

Quoted 1 line

(It's hard on the stack, yes, I know. We should fix kallsyms.)

Not just that, but it's broken when KALLSYMS is disabled. Look at what sprint_symbol() becomes.

The patch I already sent out is about a million times better, because it avoids all these issues, and knows about subtle issues like the difference between a direct pointer and a pointer to a function descriptor.

Linus

#32 lore raw

Re: the printk problem

Quoted 8 lines · Linus Torvalds
Quoted 2 lines
Quoted 2 lines

How about %p{feature}?

No.

I _expressly_ chose '%p[alphanumeric]*' because it's basically totally insane to have that in a *real* printk() string: the end result would be totally unreadable.

So, and what do you do when you run out of alphanumeric characters?

#33 lore raw

Re: the printk problem

Quoted 2 lines · Jan Engelhardt

So, and what do you do when you run out of alphanumeric characters?

Did you actually look at my patch?

It's not a single alnum character. It's an arbitrary sequence of alnum characters. IOW, my patch allows

	%p6N

or something like that for showing a ipv6 "NIP" format string etc. Or you could spell them out even more, although I consider it unlikely that you really want to see too many of these, since gcc won't actually be able to type-check them (so they will always remain _secondary_ formats).

Linus

#34 lore raw

Re: the printk problem

In reply to Linus Torvalds
Quoted 15 lines · Linus Torvalds
Quoted 2 lines · Vegard Nossum
Quoted 1 line · Jan Engelhardt

How about %p{feature}?

No.

I _expressly_ chose '%p[alphanumeric]*' because it's basically totally insane to have that in a *real* printk() string: the end result would be totally unreadable.

In contrast, '%p[specialchar]' is not unreadable, and in fact we have lots of those already in the kernel. In fact, there are 40 occurrences of '%p{' in the kernel, just grep for it (especially the AFS code seems to be very happy to use that kind of printout in its debug statements).

Quoted 8 lines

So it makes perfect sense to have a _real_ printk string that says

       "BUG: Dentry %p{i=%lx,n=%s}"

where we have that '%p{...' sequence: the end result is easily parseable. In contrast, anybody who uses '%pS' or something like that and expects a pointer name to be immediately followed by teh letter 'S' is simply insane, because the end result is an unreadable mess.

That's really a truly hideous hack :-)

Single letters are bad because it hurts readability and limits the usefulness of the extension.</MHO>

If it's just the {} that is the problem, use something else. I'm sure we can find something that isn't used already.

Quoted 5 lines
Quoted 2 lines

(It's hard on the stack, yes, I know. We should fix kallsyms.)

Not just that, but it's broken when KALLSYMS is disabled. Look at what sprint_symbol() becomes.

Oops. Not hard to mend, though.

Quoted 3 lines

The patch I already sent out is about a million times better, because it avoids all these issues, and knows about subtle issues like the difference between a direct pointer and a pointer to a function descriptor.

Right, right. I found it now:

http://ozlabs.org/pipermail/linuxppc-dev/2008-July/059257.html

Argh... Some pointer to original thread would be nice when adding new Ccs.

Vegard

PS: Your version has exactly the same stack problem. Will send a patch for kallsyms later.

Signature · 4 lines
"The animistic metaphor of the bug that maliciously sneaked in while
the programmer was not looking is intellectually dishonest as it
disguises that the error is the programmer's own creation."
	-- E. W. Dijkstra, EWD1036
#35 lore raw

Re: the printk problem

Quoted 2 lines · Vegard Nossum

Single letters are bad because it hurts readability and limits the usefulness of the extension.</MHO>

I think you need a little warning noise that goes off in your head that means "I might be overdesigning this". Linus' code is elegant and solves a problem nicely.

Signature · 4 lines
Intel are signing my paycheques ... these opinions are still mine
"Bill, look, we understand that you're interested in selling us this
operating system, but compare it to ours.  We can't possibly take such
a retrograde step."
#36 lore raw

Re: the printk problem

Quoted 2 lines · Vegard Nossum
Quoted 2 lines

Single letters are bad because it hurts readability and limits the usefulness of the extension.</MHO>

Quoted 3 lines · Matthew Wilcox

I think you need a little warning noise that goes off in your head that means "I might be overdesigning this". Linus' code is elegant and solves a problem nicely.

Am I the only one who missed Linus' patch? Did it make it to the list?

#37 lore raw

Re: the printk problem

Quoted 10 lines · On Sun, 6 Jul 2008 03:02:59 +0300 Pekka Enberg wrote:
Quoted 2 lines · Vegard Nossum
Quoted 2 lines

Single letters are bad because it hurts readability and limits the usefulness of the extension.</MHO>

Quoted 3 lines · Matthew Wilcox

I think you need a little warning noise that goes off in your head that means "I might be overdesigning this". Linus' code is elegant and solves a problem nicely.

Am I the only one who missed Linus' patch? Did it make it to the list?

No, you are not the only one. It was on linuxppc-dev for some reason.
http://ozlabs.org/pipermail/linuxppc-dev/2008-July/059257.html

---
~Randy
Linux Plumbers Conference, 17-19 September 2008, Portland, Oregon USA
http://linuxplumbersconf.org/

#38 lore raw

Re: the printk problem

In reply to Matthew Wilcox
Quoted 3 lines

u64 is easy to fix, and I don't know why we haven't. Just make it unsigned long long on all architectures.

Yup. Also, one of the major user of that is to print a struct resource, which everybody does differently, so we may even look at doing a %pR that does the nice start..end [attr]..

Ben.

#39 lore raw

Re: the printk problem

In reply to Linus Torvalds
Quoted 9 lines · Linus Torvalds
Quoted 7 lines · Linus Torvalds

so I think we could easily just say that we extend %p in various ways:

- %pS - print pointer as a symbol

and leave tons of room for future extensions for different kinds of pointers.

Quoted 6 lines

So here's a totally untested example patch of this, which could probably easily be extended to to other things.

I actually made it '%pF' and '%pS' for a Function descriptor pointer and normal Symbolic pointer respectively, because of the stupid things ia64 and PPC64 do with the pointer indirection through function descriptors.

It's also true for parisc, fwiw. Added a cc to them.

Quoted 187 lines

That function descriptor indirection is totally untested, and I did it with a

	pagefault_disable();
	__get_user(..)
	pagefault_enable();

thing because I thought it would be nice if printk() was always safe, and passing bogus function pointers to '%pF' should try to work, but quite frankly, I didn't even check that that part compiles, much less works.

ia64/ppc lists cc'd, just in case somebody wants to test this.

NOTE! There are no current actual users of this, but the _idea_ is that we should be able to just do

	printk("%pF\n", desc->handle_irq);

instead of using

	print_symbol("%s\n", (unsigned long)desc->handle_irq);

The latter is from kernel/irq/internals.h, and actually looks incorrect - shouldn't it use print_fn_descriptor_symbol(), since it's a C level function pointer? We should use "print_symbol()" for return pointers we find on the stack and data pointers, but not for stuff that actually has a C type that is a function pointer?

Somebody who cares about the insane function descriptors should take a deeper look.

NOTE AGAIN! UNTESTED! I could easily have screwed up printk() _entirely_, since I had to factor out the string handling into a function of its own.

Linus

---
 lib/vsprintf.c |  102 ++++++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 74 insertions(+), 28 deletions(-)
lib/vsprintf.c +74 -29
index 6021757..148b656 100644
@@ -22,6 +22,7 @@
 #include <linux/string.h>
 #include <linux/ctype.h>
 #include <linux/kernel.h>
+#include <linux/kallsyms.h>
 
 #include <asm/page.h>		/* for PAGE_SIZE */
 #include <asm/div64.h>
@@ -482,6 +483,72 @@ static char *number(char *buf, char *end, unsigned long long num, int base, int
 	return buf;
 }
 
+static char *string(char *buf, char *end, char *s, int field_width, int precision, int flags)
+{
+	int len, i;
+
+	if ((unsigned long)s < PAGE_SIZE)
+		s = "<NULL>";
+
+	len = strnlen(s, precision);
+
+	if (!(flags & LEFT)) {
+		while (len < field_width--) {
+			if (buf < end)
+				*buf = ' ';
+			++buf;
+		}
+	}
+	for (i = 0; i < len; ++i) {
+		if (buf < end)
+			*buf = *s;
+		++buf; ++s;
+	}
+	while (len < field_width--) {
+		if (buf < end)
+			*buf = ' ';
+		++buf;
+	}
+	return buf;
+}
+
+/*
+ * Show a '%p' thing.  A kernel extension is that the '%p' is followed
+ * by an extra set of alphanumeric characters that are extended format
+ * specifiers.  Right now we just handle 'F' (for symbolic Function
+ * pointers) and 'S' (for Symbolic data pointers), but this can easily
+ * be extended in the future (network address types etc).
+ *
+ * The difference between 'S' and 'F' is that on ia64 and ppc64 function
+ * pointers are really function descriptors, which contain a pointer the
+ * real address. 
+ */
+static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int base, int size, int precision, int type)
+{
+	char sym[KSYM_SYMBOL_LEN];
+	switch (*fmt) {
+	case 'F':	/* Function pointer */
+#if defined(CONFIG_IA64) || defined(CONFIG_PPC64)
+		{ void *p;
+		pagefault_disable();
+		if (!__get_user((void **)ptr, &p))
+			ptr = p;
+		pagefault_enable();
+		}
+#endif
+	/* Fallthrough */
+	case 'S':	/* Other pointer */
+#if CONFIG_KALLSYMS
+		sprint_symbol(sym, (unsigned long) ptr);
+		return string(buf, end, sym, size, precision, type);
+#else
+		type |= SPECIAL;
+		break;
+#endif
+	}
+	return number(buf, end, (unsigned long long) ptr, base, size, precision, type);
+}
+
 /**
  * vsnprintf - Format a string and place it in a buffer
  * @buf: The buffer to place the result into
@@ -502,11 +569,9 @@ static char *number(char *buf, char *end, unsigned long long num, int base, int
  */
 int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 {
-	int len;
 	unsigned long long num;
-	int i, base;
+	int base;
 	char *str, *end, c;
-	const char *s;
 
 	int flags;		/* flags to number() */
 
@@ -622,29 +687,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 				continue;
 
 			case 's':
-				s = va_arg(args, char *);
-				if ((unsigned long)s < PAGE_SIZE)
-					s = "<NULL>";
-
-				len = strnlen(s, precision);
-
-				if (!(flags & LEFT)) {
-					while (len < field_width--) {
-						if (str < end)
-							*str = ' ';
-						++str;
-					}
-				}
-				for (i = 0; i < len; ++i) {
-					if (str < end)
-						*str = *s;
-					++str; ++s;
-				}
-				while (len < field_width--) {
-					if (str < end)
-						*str = ' ';
-					++str;
-				}
+				str = string(str, end, va_arg(args, char *), field_width, precision, flags);
 				continue;
 
 			case 'p':
@@ -653,9 +696,12 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 					field_width = 2*sizeof(void *);
 					flags |= ZEROPAD;
 				}
-				str = number(str, end,
-						(unsigned long) va_arg(args, void *),
+				str = pointer(fmt+1, str, end,
+						va_arg(args, void *),
 						16, field_width, precision, flags);
+				/* Skip all alphanumeric pointer suffixes */
+				while (isalnum(fmt[1]))
+					fmt++;
 				continue;
 
 
--

To unsubscribe from this list: send the line "unsubscribe linux-ia64" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Signature · 4 lines
Intel are signing my paycheques ... these opinions are still mine
"Bill, look, we understand that you're interested in selling us this
operating system, but compare it to ours.  We can't possibly take such
a retrograde step."
#40 lore raw

Re: the printk problem

Quoted 2 lines · Matthew Wilcox

It's also true for parisc, fwiw. Added a cc to them.

I posted a patch months ago for kallsyms on parisc, but it looks like nobody ever responded or cared. Nice.

#41 lore raw

Re: the printk problem

In reply to Linus Torvalds
Quoted 8 lines · Linus Torvalds

That function descriptor indirection is totally untested, and I did it with a

        pagefault_disable();
        __get_user(..)
        pagefault_enable();
Quoted 5 lines

thing because I thought it would be nice if printk() was always safe, and passing bogus function pointers to '%pF' should try to work, but quite frankly, I didn't even check that that part compiles, much less works.

ia64/ppc lists cc'd, just in case somebody wants to test this.

I'll give it a try using probe_kernel_address() instead on monday.

Cheers,
Ben.

#42 lore raw

Re: the printk problem

Quoted 2 lines · Benjamin Herrenschmidt

I'll give it a try using probe_kernel_address() instead on monday.

Here's the updated patch which uses probe_kernel_address() instead (and moves the whole #ifdef mess out of the code that wants it and into a helper function - and maybe we should then put that helper into kallsyms.h, but that's a different issue).

Still all happily untested, of course. And still with no actual users converted.

Linus

---
 lib/vsprintf.c |  108 +++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 80 insertions(+), 28 deletions(-)
lib/vsprintf.c +80 -28
index 6021757..1fbb1c0 100644
@@ -22,6 +22,8 @@
 #include <linux/string.h>
 #include <linux/ctype.h>
 #include <linux/kernel.h>
+#include <linux/kallsyms.h>
+#include <linux/uaccess.h>
 
 #include <asm/page.h>		/* for PAGE_SIZE */
 #include <asm/div64.h>
@@ -482,6 +484,77 @@ static char *number(char *buf, char *end, unsigned long long num, int base, int
 	return buf;
 }
 
+static char *string(char *buf, char *end, char *s, int field_width, int precision, int flags)
+{
+	int len, i;
+
+	if ((unsigned long)s < PAGE_SIZE)
+		s = "<NULL>";
+
+	len = strnlen(s, precision);
+
+	if (!(flags & LEFT)) {
+		while (len < field_width--) {
+			if (buf < end)
+				*buf = ' ';
+			++buf;
+		}
+	}
+	for (i = 0; i < len; ++i) {
+		if (buf < end)
+			*buf = *s;
+		++buf; ++s;
+	}
+	while (len < field_width--) {
+		if (buf < end)
+			*buf = ' ';
+		++buf;
+	}
+	return buf;
+}
+
+static inline void *dereference_function_descriptor(void *ptr)
+{
+#if defined(CONFIG_IA64) || defined(CONFIG_PPC64)
+	void *p;
+	if (!probe_kernel_address(ptr, p))
+		ptr = p;
+#endif
+	return ptr;
+}
+
+
+/*
+ * Show a '%p' thing.  A kernel extension is that the '%p' is followed
+ * by an extra set of alphanumeric characters that are extended format
+ * specifiers.  Right now we just handle 'F' (for symbolic Function
+ * pointers) and 'S' (for Symbolic data pointers), but this can easily
+ * be extended in the future (network address types etc).
+ *
+ * The difference between 'S' and 'F' is that on ia64 and ppc64 function
+ * pointers are really function descriptors, which contain a pointer the
+ * real address. 
+ */
+static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int base, int size, int precision, int type)
+{
+	switch (*fmt) {
+	case 'F':
+		ptr = dereference_function_descriptor(ptr);
+		/* Fallthrough */
+	case 'S': {	/* Other (direct) pointer */
+#if CONFIG_KALLSYMS
+		char sym[KSYM_SYMBOL_LEN];
+		sprint_symbol(sym, (unsigned long) ptr);
+		return string(buf, end, sym, size, precision, type);
+#else
+		type |= SPECIAL;
+		break;
+#endif
+	}
+	}
+	return number(buf, end, (unsigned long long) ptr, base, size, precision, type);
+}
+
 /**
  * vsnprintf - Format a string and place it in a buffer
  * @buf: The buffer to place the result into
@@ -502,11 +575,9 @@ static char *number(char *buf, char *end, unsigned long long num, int base, int
  */
 int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 {
-	int len;
 	unsigned long long num;
-	int i, base;
+	int base;
 	char *str, *end, c;
-	const char *s;
 
 	int flags;		/* flags to number() */
 
@@ -622,29 +693,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 				continue;
 
 			case 's':
-				s = va_arg(args, char *);
-				if ((unsigned long)s < PAGE_SIZE)
-					s = "<NULL>";
-
-				len = strnlen(s, precision);
-
-				if (!(flags & LEFT)) {
-					while (len < field_width--) {
-						if (str < end)
-							*str = ' ';
-						++str;
-					}
-				}
-				for (i = 0; i < len; ++i) {
-					if (str < end)
-						*str = *s;
-					++str; ++s;
-				}
-				while (len < field_width--) {
-					if (str < end)
-						*str = ' ';
-					++str;
-				}
+				str = string(str, end, va_arg(args, char *), field_width, precision, flags);
 				continue;
 
 			case 'p':
@@ -653,9 +702,12 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 					field_width = 2*sizeof(void *);
 					flags |= ZEROPAD;
 				}
-				str = number(str, end,
-						(unsigned long) va_arg(args, void *),
+				str = pointer(fmt+1, str, end,
+						va_arg(args, void *),
 						16, field_width, precision, flags);
+				/* Skip all alphanumeric pointer suffixes */
+				while (isalnum(fmt[1]))
+					fmt++;
 				continue;
 
 
#43 lore raw

Re: the printk problem

Quoted 3 lines · Linus Torvalds

Still all happily untested, of course. And still with no actual users converted.

Ok, it's tested, and here's an example usage conversion.

The diffstat pretty much says it all. It _does_ change the format of the stack trace entry a bit, but I don't think it's for the worse (unless it breaks things like the oops tracker - Arjan?)

It changes the symbol-in-module format from

	:ext3:add_dirent_to_buf+0x6c/0x26c

to

	add_dirent_to_buf+0x6c/0x26c [ext3]

but quite frankly, the latter was the standard format anyway (it's what "sprint_symbol()" gives you), and traps_64.c was the odd man out.

In fact, traps_32.c already uses the standard print_symbol() format, so it really was an issue of the 64-bit code being odd (and I assume that this also means that it cannot break the oops tracker, since it already had to be able to handle both formats).

I also removed the KALLSYMS dependency, so if KALLSYMS isn't enabled it will now give the same hex format twice, but I doubt we really care (such stack traces are unreadable whether it shows up once or twice, and the simplicity is worth it).

If people do just a few more conversions like this, then the 52 added lines in lib/vsnprintf.c are more than made up for by removed lines elsewhere (and more readable source code).

Linus

---
 arch/x86/kernel/traps_64.c |   25 +------------------------
 1 files changed, 1 insertions(+), 24 deletions(-)
arch/x86/kernel/traps_64.c +1 -24
index adff76e..f1a95d1 100644
@@ -104,30 +104,7 @@ int kstack_depth_to_print = 12;
 
 void printk_address(unsigned long address, int reliable)
 {
-#ifdef CONFIG_KALLSYMS
-	unsigned long offset = 0, symsize;
-	const char *symname;
-	char *modname;
-	char *delim = ":";
-	char namebuf[KSYM_NAME_LEN];
-	char reliab[4] = "";
-
-	symname = kallsyms_lookup(address, &symsize, &offset,
-					&modname, namebuf);
-	if (!symname) {
-		printk(" [<%016lx>]\n", address);
-		return;
-	}
-	if (!reliable)
-		strcpy(reliab, "? ");
-
-	if (!modname)
-		modname = delim = "";
-	printk(" [<%016lx>] %s%s%s%s%s+0x%lx/0x%lx\n",
-		address, reliab, delim, modname, delim, symname, offset, symsize);
-#else
-	printk(" [<%016lx>]\n", address);
-#endif
+	printk(" [<%016lx>] %s%pS\n", address, reliable ? "": "? ", (void *) address);
 }
 
 static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack,
#44 lore raw

Re: the printk problem

Quoted 13 lines · Linus Torvalds
Quoted 2 lines · Linus Torvalds

Still all happily untested, of course. And still with no actual users converted.

Ok, it's tested, and here's an example usage conversion.

The diffstat pretty much says it all. It _does_ change the format of the stack trace entry a bit, but I don't think it's for the worse (unless it breaks things like the oops tracker - Arjan?)

It changes the symbol-in-module format from

Quoted 9 lines
	:ext3:add_dirent_to_buf+0x6c/0x26c

to

	add_dirent_to_buf+0x6c/0x26c [ext3]

but quite frankly, the latter was the standard format anyway (it's what "sprint_symbol()" gives you), and traps_64.c was the odd man out.

This won't break anything for me actually; I already deal with either case.

$ cat oopsparse.pl | wc -l
1252

the kernel is so inconsistent with oops formats (over time/across architectures) that once you deal with what there is today... you pretty much deal with everything.

I also like the improvement; I wished something like this existed several times already in the last few months so for sure

#45 lore raw

Re: the printk problem

In reply to Linus Torvalds
Quoted 25 lines · Linus Torvalds
Quoted 2 lines

Still all happily untested, of course. And still with no actual users converted.

Ok, it's tested, and here's an example usage conversion.

The diffstat pretty much says it all. It _does_ change the format of the stack trace entry a bit, but I don't think it's for the worse (unless it breaks things like the oops tracker - Arjan?)

It changes the symbol-in-module format from

	:ext3:add_dirent_to_buf+0x6c/0x26c

to

	add_dirent_to_buf+0x6c/0x26c [ext3]

but quite frankly, the latter was the standard format anyway (it's what "sprint_symbol()" gives you), and traps_64.c was the odd man out.

In fact, traps_32.c already uses the standard print_symbol() format, so it really was an issue of the 64-bit code being odd (and I assume that this also means that it cannot break the oops tracker, since it already had to be able to handle both formats).

Quoted 8 lines

I also removed the KALLSYMS dependency, so if KALLSYMS isn't enabled it will now give the same hex format twice, but I doubt we really care (such stack traces are unreadable whether it shows up once or twice, and the simplicity is worth it).

If people do just a few more conversions like this, then the 52 added lines in lib/vsnprintf.c are more than made up for by removed lines elsewhere (and more readable source code).

applied (with the commit message below) to tip/x86/debug for v2.6.27 merging, thanks Linus. Can i add your SOB too?

Ingo

-------------------->
commit 4afd2534d6d4a77f4b7497c92f1ff7528d8f4eaa

Author: Linus Torvalds <[email protected]>
Date:   Sat Jul 5 15:32:41 2008 -0700

x86, 64-bit: standardize printk_address()

Changes the symbol-in-module format from

:ext3:add_dirent_to_buf+0x6c/0x26c

to

add_dirent_to_buf+0x6c/0x26c [ext3]

the latter was the standard format anyway (it's what "sprint_symbol()" gives you), and traps_64.c was the odd man out.

In fact, traps_32.c already uses the standard print_symbol() format, so it really was an issue of the 64-bit code being odd (and I assume that this also means that it cannot break the oops tracker, since it already had to be able to handle both formats).

I also removed the KALLSYMS dependency, so if KALLSYMS isn't enabled it will now give the same hex format twice, but I doubt we really care (such stack traces are unreadable whether it shows up once or twice, and the simplicity is worth it).

If people do just a few more conversions like this, then the 52 added lines in lib/vsnprintf.c are more than made up for by removed lines elsewhere (and more readable source code).

arch/x86/kernel/traps_64.c +1 -24
index adff76e..f1a95d1 100644
@@ -104,30 +104,7 @@ int kstack_depth_to_print = 12;
 
 void printk_address(unsigned long address, int reliable)
 {
-#ifdef CONFIG_KALLSYMS
-	unsigned long offset = 0, symsize;
-	const char *symname;
-	char *modname;
-	char *delim = ":";
-	char namebuf[KSYM_NAME_LEN];
-	char reliab[4] = "";
-
-	symname = kallsyms_lookup(address, &symsize, &offset,
-					&modname, namebuf);
-	if (!symname) {
-		printk(" [<%016lx>]\n", address);
-		return;
-	}
-	if (!reliable)
-		strcpy(reliab, "? ");
-
-	if (!modname)
-		modname = delim = "";
-	printk(" [<%016lx>] %s%s%s%s%s+0x%lx/0x%lx\n",
-		address, reliab, delim, modname, delim, symname, offset, symsize);
-#else
-	printk(" [<%016lx>]\n", address);
-#endif
+	printk(" [<%016lx>] %s%pS\n", address, reliable ? "": "? ", (void *) address);
 }
 
 static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack,
#46 lore raw

Re: the printk problem

Quoted 3 lines · Ingo Molnar

applied (with the commit message below) to tip/x86/debug for v2.6.27 merging, thanks Linus. Can i add your SOB too?

Sure, add my S-O-B. But I hope/assuem that you also added my earlier patch that added the support for '%pS' too? I'm not entirely sure that should go in an x86-specific branch, since it has nothing x86-specific in it.

Also, I've cleaned up the lib/vsnprintf.c patch to fix up some minor details. For example, it should not default to a field width of "2*sizeof(unsigned long)" - it should do that only if it ends up printing the pointer as a hex number.

So this is my current version.. (the "precision" thing was moved into the 'pointer()' function, and to after the special case handling).

Linus

---
 lib/vsprintf.c |  118 ++++++++++++++++++++++++++++++++++++++++----------------
 1 files changed, 85 insertions(+), 33 deletions(-)
lib/vsprintf.c +85 -33
index 6021757..f60c7c0 100644
@@ -22,6 +22,8 @@
 #include <linux/string.h>
 #include <linux/ctype.h>
 #include <linux/kernel.h>
+#include <linux/kallsyms.h>
+#include <linux/uaccess.h>
 
 #include <asm/page.h>		/* for PAGE_SIZE */
 #include <asm/div64.h>
@@ -482,6 +484,82 @@ static char *number(char *buf, char *end, unsigned long long num, int base, int
 	return buf;
 }
 
+static char *string(char *buf, char *end, char *s, int field_width, int precision, int flags)
+{
+	int len, i;
+
+	if ((unsigned long)s < PAGE_SIZE)
+		s = "<NULL>";
+
+	len = strnlen(s, precision);
+
+	if (!(flags & LEFT)) {
+		while (len < field_width--) {
+			if (buf < end)
+				*buf = ' ';
+			++buf;
+		}
+	}
+	for (i = 0; i < len; ++i) {
+		if (buf < end)
+			*buf = *s;
+		++buf; ++s;
+	}
+	while (len < field_width--) {
+		if (buf < end)
+			*buf = ' ';
+		++buf;
+	}
+	return buf;
+}
+
+static inline void *dereference_function_descriptor(void *ptr)
+{
+#if defined(CONFIG_IA64) || defined(CONFIG_PPC64)
+	void *p;
+	if (!probe_kernel_address(ptr, p))
+		ptr = p;
+#endif
+	return ptr;
+}
+
+
+/*
+ * Show a '%p' thing.  A kernel extension is that the '%p' is followed
+ * by an extra set of alphanumeric characters that are extended format
+ * specifiers.  Right now we just handle 'F' (for symbolic Function
+ * pointers) and 'S' (for Symbolic data pointers), but this can easily
+ * be extended in the future (network address types etc).
+ *
+ * The difference between 'S' and 'F' is that on ia64 and ppc64 function
+ * pointers are really function descriptors, which contain a pointer the
+ * real address. 
+ */
+static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int base, int size, int precision, int type)
+{
+	switch (*fmt) {
+	case 'F':
+		ptr = dereference_function_descriptor(ptr);
+		/* Fallthrough */
+	case 'S': {	/* Other (direct) pointer */
+#if CONFIG_KALLSYMS
+		char sym[KSYM_SYMBOL_LEN];
+		sprint_symbol(sym, (unsigned long) ptr);
+		return string(buf, end, sym, size, precision, type);
+#else
+		type |= SPECIAL;
+		break;
+#endif
+	}
+	}
+	type |= SMALL;
+	if (precision = -1) {
+		precision = 2*sizeof(void *);
+		type |= ZEROPAD;
+	}
+	return number(buf, end, (unsigned long long) ptr, base, size, precision, type);
+}
+
 /**
  * vsnprintf - Format a string and place it in a buffer
  * @buf: The buffer to place the result into
@@ -502,11 +580,9 @@ static char *number(char *buf, char *end, unsigned long long num, int base, int
  */
 int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 {
-	int len;
 	unsigned long long num;
-	int i, base;
+	int base;
 	char *str, *end, c;
-	const char *s;
 
 	int flags;		/* flags to number() */
 
@@ -622,40 +698,16 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
 				continue;
 
 			case 's':
-				s = va_arg(args, char *);
-				if ((unsigned long)s < PAGE_SIZE)
-					s = "<NULL>";
-
-				len = strnlen(s, precision);
-
-				if (!(flags & LEFT)) {
-					while (len < field_width--) {
-						if (str < end)
-							*str = ' ';
-						++str;
-					}
-				}
-				for (i = 0; i < len; ++i) {
-					if (str < end)
-						*str = *s;
-					++str; ++s;
-				}
-				while (len < field_width--) {
-					if (str < end)
-						*str = ' ';
-					++str;
-				}
+				str = string(str, end, va_arg(args, char *), field_width, precision, flags);
 				continue;
 
 			case 'p':
-				flags |= SMALL;
-				if (field_width = -1) {
-					field_width = 2*sizeof(void *);
-					flags |= ZEROPAD;
-				}
-				str = number(str, end,
-						(unsigned long) va_arg(args, void *),
+				str = pointer(fmt+1, str, end,
+						va_arg(args, void *),
 						16, field_width, precision, flags);
+				/* Skip all alphanumeric pointer suffixes */
+				while (isalnum(fmt[1]))
+					fmt++;
 				continue;
 
 
#47 lore raw

Re: the printk problem

Quoted 9 lines · Linus Torvalds
Quoted 3 lines · Ingo Molnar

applied (with the commit message below) to tip/x86/debug for v2.6.27 merging, thanks Linus. Can i add your SOB too?

Sure, add my S-O-B. But I hope/assuem that you also added my earlier patch that added the support for '%pS' too? I'm not entirely sure that should go in an x86-specific branch, since it has nothing x86-specific in it.

yeah, agreed, combined it's not an x86 topic anymore.

[ There's some lkml trouble so i've missed the earlier patch. I'm not
sure the email problem is on my side, see how incomplete the discussion is on lkml.org as well:

     http://lkml.org/lkml/2008/6/25/170   ]

Anyway, i have have added this second patch of yours to tip/core/printk and moved your first patch over to that topic (which relies on it).

That topic has a few other (smaller) printk enhancements queued for v2.6.27 already:

git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip.git core/printk

[find other stats below]

so it fits in naturally.

Ingo

------------------>
Ingo Molnar (1):
printk: export console_drivers

Jan Kiszka (1):
printk: don't prefer unsuited consoles on registration

Jiri Slaby (1):
x86, generic: mark early_printk as asmlinkage

Linus Torvalds (2):
printk: add support for '%pS'
x86, 64-bit: standardize printk_address()

Nick Andrew (2):
printk: refactor processing of line severity tokens
printk: remember the message level for multi-line output

Tejun Heo (1):
printk: clean up recursion check related static variables

Thomas Gleixner (2):
namespacecheck: fix kernel printk.c
namespacecheck: more kernel/printk.c fixes

 arch/x86/kernel/early_printk.c |    2 +-
 arch/x86/kernel/traps_64.c     |   25 +--------
 include/linux/kernel.h         |    8 +---
 kernel/printk.c                |  107 +++++++++++++++---------------------
 lib/vsprintf.c                 |  118 +++++++++++++++++++++++++++++-----------
 5 files changed, 133 insertions(+), 127 deletions(-)
arch/x86/kernel/early_printk.c +1 -1
index 643fd86..ff9e735 100644
@@ -196,7 +196,7 @@ static struct console simnow_console = {
 static struct console *early_console = &early_vga_console;
 static int early_console_initialized;
 
-void early_printk(const char *fmt, ...)
+asmlinkage void early_printk(const char *fmt, ...)
 {
 	char buf[512];
 	int n;
arch/x86/kernel/traps_64.c +1 -24
index adff76e..f1a95d1 100644
@@ -104,30 +104,7 @@ int kstack_depth_to_print = 12;
 
 void printk_address(unsigned long address, int reliable)
 {
-#ifdef CONFIG_KALLSYMS
-	unsigned long offset = 0, symsize;
-	const char *symname;
-	char *modname;
-	char *delim = ":";
-	char namebuf[KSYM_NAME_LEN];
-	char reliab[4] = "";
-
-	symname = kallsyms_lookup(address, &symsize, &offset,
-					&modname, namebuf);
-	if (!symname) {
-		printk(" [<%016lx>]\n", address);
-		return;
-	}
-	if (!reliable)
-		strcpy(reliab, "? ");
-
-	if (!modname)
-		modname = delim = "";
-	printk(" [<%016lx>] %s%s%s%s%s+0x%lx/0x%lx\n",
-		address, reliab, delim, modname, delim, symname, offset, symsize);
-#else
-	printk(" [<%016lx>]\n", address);
-#endif
+	printk(" [<%016lx>] %s%pS\n", address, reliable ? "": "? ", (void *) address);
 }
 
 static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack,
include/linux/kernel.h +1 -7
index 792bf0a..4cb8d3d 100644
@@ -184,9 +184,6 @@ asmlinkage int vprintk(const char *fmt, va_list args)
 	__attribute__ ((format (printf, 1, 0)));
 asmlinkage int printk(const char * fmt, ...)
 	__attribute__ ((format (printf, 1, 2))) __cold;
-extern int log_buf_get_len(void);
-extern int log_buf_read(int idx);
-extern int log_buf_copy(char *dest, int idx, int len);
 
 extern int printk_ratelimit_jiffies;
 extern int printk_ratelimit_burst;
@@ -202,9 +199,6 @@ static inline int vprintk(const char *s, va_list args) { return 0; }
 static inline int printk(const char *s, ...)
 	__attribute__ ((format (printf, 1, 2)));
 static inline int __cold printk(const char *s, ...) { return 0; }
-static inline int log_buf_get_len(void) { return 0; }
-static inline int log_buf_read(int idx) { return 0; }
-static inline int log_buf_copy(char *dest, int idx, int len) { return 0; }
 static inline int printk_ratelimit(void) { return 0; }
 static inline int __printk_ratelimit(int ratelimit_jiffies, \
 				     int ratelimit_burst) { return 0; }
@@ -213,7 +207,7 @@ static inline bool printk_timed_ratelimit(unsigned long *caller_jiffies, \
 		{ return false; }
 #endif
 
-extern void __attribute__((format(printf, 1, 2)))
+extern void asmlinkage __attribute__((format(printf, 1, 2)))
 	early_printk(const char *fmt, ...);
 
 unsigned long int_sqrt(unsigned long);
kernel/printk.c +44 -61

185 lines not on this page · raw message

lib/vsprintf.c +85 -33

148 lines not on this page · raw message

#48 lore raw

Re: the printk problem

Quoted 11 lines · Ingo Molnar

yeah, agreed, combined it's not an x86 topic anymore.

[ There's some lkml trouble so i've missed the earlier patch. I'm not
sure the email problem is on my side, see how incomplete the discussion is on lkml.org as well:

     http://lkml.org/lkml/2008/6/25/170   ]

Anyway, i have have added this second patch of yours to tip/core/printk and moved your first patch over to that topic (which relies on it).

ah, i found the reason - it started out on linux-ia64 originally then moved over to lkml - so only half of the discussion was visible there. And linux-ia64 is one of the few vger lists i'm not subscribed to apparently. (there's no vger-please-give-me-all-emails list - making the following of Linux development even harder)

Ingo

#49 lore raw

Re: the printk problem

In reply to Linus Torvalds
Quoted 5 lines · Linus Torvalds
Quoted 2 lines · Benjamin Herrenschmidt

I'll give it a try using probe_kernel_address() instead on monday.

Quoted 7 lines

Here's the updated patch which uses probe_kernel_address() instead (and moves the whole #ifdef mess out of the code that wants it and into a helper function - and maybe we should then put that helper into kallsyms.h, but that's a different issue).

Still all happily untested, of course. And still with no actual users converted.

Did a few tests and it seems to work. I'll stick a patch converting powerpc to use %pS for oops display in -next.

Thanks !

Cheers,
Ben.

#50 lore raw

Re: the printk problem

Quoted 12 lines · On Mon, 07 Jul 2008 11:14:36 +1000 Benjamin Herrenschmidt…
Quoted 10 lines · Linus Torvalds
Quoted 2 lines · Benjamin Herrenschmidt

I'll give it a try using probe_kernel_address() instead on monday.

Here's the updated patch which uses probe_kernel_address() instead (and moves the whole #ifdef mess out of the code that wants it and into a helper function - and maybe we should then put that helper into kallsyms.h, but that's a different issue).

Quoted 5 lines
Quoted 2 lines

Still all happily untested, of course. And still with no actual users converted.

Did a few tests and it seems to work. I'll stick a patch converting powerpc to use %pS for oops display in -next.

After you post it to linuxppc-dev and get review comments, of course ...

And after the patch to make %pS work goes in ...

Signature · 3 lines
Cheers,
Stephen Rothwell                    [email protected]
http://www.canb.auug.org.au/~sfr/
#51 lore raw

Re: the printk problem

Quoted 16 lines · Stephen Rothwell
Quoted 15 lines · On Mon, 07 Jul 2008 11:14:36 +1000 Benjamin Herrenschmidt…
Quoted 12 lines · Linus Torvalds
Quoted 2 lines · Benjamin Herrenschmidt

I'll give it a try using probe_kernel_address() instead on monday.

Here's the updated patch which uses probe_kernel_address() instead (and moves the whole #ifdef mess out of the code that wants it and into a helper function - and maybe we should then put that helper into kallsyms.h, but that's a different issue).

Still all happily untested, of course. And still with no actual users converted.

Quoted 6 lines
Quoted 2 lines

Did a few tests and it seems to work. I'll stick a patch converting powerpc to use %pS for oops display in -next.

After you post it to linuxppc-dev and get review comments, of course ...

And after the patch to make %pS work goes in ...

Wasn't that already merged via the trivial scheduler fixes tree or something? ;)

cheers

Signature · 8 lines
Michael Ellerman
OzLabs, IBM Australia Development Lab

wwweb: http://michael.ellerman.id.au
phone: +61 2 6212 1183 (tie line 70 21183)

We do not inherit the earth from our ancestors,
we borrow it from our children. - S.M.A.R.T Person
#52 lore raw

Re: the printk problem

Quoted 3 lines · On Mon, 07 Jul 2008 13:28:18 +1000 Michael Ellerman <[email protected]>…

Wasn't that already merged via the trivial scheduler fixes tree or something? ;)

Not yet.

Signature · 3 lines
Cheers,
Stephen Rothwell                    [email protected]
http://www.canb.auug.org.au/~sfr/
#53 lore raw

Re: the printk problem

In reply to Stephen Rothwell
Quoted 5 lines · Stephen Rothwell
Quoted 2 lines

Did a few tests and it seems to work. I'll stick a patch converting powerpc to use %pS for oops display in -next.

After you post it to linuxppc-dev and get review comments, of course ...

I though I did that already, looks like I didn't. Let me see what went wrong..

Quoted 1 line

And after the patch to make %pS work goes in ...

Yup.

Ben.