LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
From: venkatesh.pallipadi@intel.com
To: ak@muc.de, ebiederm@xmission.com, rdreier@cisco.com,
	torvalds@linux-foundation.org, gregkh@suse.de, airlied@skynet.ie,
	davej@redhat.com, mingo@elte.hu, tglx@linutronix.de,
	hpa@zytor.com, akpm@linux-foundation.org, arjan@infradead.org,
	jesse.barnes@intel.com, davem@davemloft.net
Cc: linux-kernel@vger.kernel.org,
	Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>,
	Suresh Siddha <suresh.b.siddha@intel.com>
Subject: [patch 01/11] PAT x86: Make acpi/other drivers map memory instead of assuming identity map
Date: Thu, 10 Jan 2008 10:48:41 -0800	[thread overview]
Message-ID: <20080110184854.557190000@intel.com> (raw)
In-Reply-To: <20080110184840.927409000@intel.com>

[-- Attachment #1: map_instead_of_va.patch --]
[-- Type: text/plain, Size: 14407 bytes --]


This series is heavily derived from the PAT patchset by Eric Biederman and
Andi Kleen.
http://www.firstfloor.org/pub/ak/x86_64/pat/

This patchset is a followup of "PAT support for X86_64"
http://www.ussg.iu.edu/hypermail/linux/kernel/0712.1/2268.html

Things changed from the above (Dec 13 2007) version:
* PAT mappings now used are - (0,WB) (1,WT) (2,WC) (3,UC).
* Covers both i386 and x86_64.
* Resolve the /sysfs issue by exporting wc and uc interfaces.
* Piggyback PAT initialization on existing MTRR initialization as they
  have same setup rules.
* Avoid early table allocation problem for x86_64 by doing the reserved
  region pruning later in the boot. Handle both memory identity mapping and
  kernel test mapping.
* Handle fork() and /dev/mem mapping and unmapping cases.

This patch:

Some boot code has assumptions about entire memory being mapped in identity
mapping. Fix them to use some form of mapping instead. Places fixed below:
* Generic __acpi_map_table
* Looking for RSD PTR at boot time
* Looking for mp table
* get_bios_ebda and ebda size
* pci-calgary (Compile tested only. Will be great if someone who has this
               hardware can verify that this change works fine)
              (This patch is testable as a standalone patch)

Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
---

Patchset is against Ingo's x86 branch from 2 days ago. Will need some merging
effort with Andi's CPA changes and few other changes like pgtable.h unification.


Index: linux-2.6.git/arch/x86/kernel/acpi/boot.c
===================================================================
--- linux-2.6.git.orig/arch/x86/kernel/acpi/boot.c	2008-01-08 03:31:31.000000000 -0800
+++ linux-2.6.git/arch/x86/kernel/acpi/boot.c	2008-01-08 03:43:46.000000000 -0800
@@ -105,16 +105,20 @@
 
 #ifdef	CONFIG_X86_64
 
-/* rely on all ACPI tables being in the direct mapping */
 char *__acpi_map_table(unsigned long phys_addr, unsigned long size)
 {
 	if (!phys_addr || !size)
 		return NULL;
 
-	if (phys_addr+size <= (end_pfn_map << PAGE_SHIFT) + PAGE_SIZE)
-		return __va(phys_addr);
+	return early_ioremap(phys_addr, size);
+}
 
-	return NULL;
+void __acpi_unmap_table(void * addr, unsigned long size)
+{
+	if (!addr || !size)
+		return;
+
+	early_iounmap(addr, size);
 }
 
 #else
@@ -158,6 +162,11 @@
 
 	return ((unsigned char *)base + offset);
 }
+
+void __acpi_unmap_table(void * addr, unsigned long size)
+{
+}
+
 #endif
 
 #ifdef CONFIG_PCI_MMCONFIG
@@ -586,17 +595,23 @@
 {
 	unsigned long offset = 0;
 	unsigned long sig_len = sizeof("RSD PTR ") - 1;
+	char * virt_addr;
 
+	virt_addr = __acpi_map_table(start, length);
+	if (!virt_addr)
+		return 0;
 	/*
 	 * Scan all 16-byte boundaries of the physical memory region for the
 	 * RSDP signature.
 	 */
 	for (offset = 0; offset < length; offset += 16) {
-		if (strncmp((char *)(phys_to_virt(start) + offset), "RSD PTR ", sig_len))
+		if (strncmp(virt_addr + offset, "RSD PTR ", sig_len))
 			continue;
+		__acpi_unmap_table(virt_addr, length);
 		return (start + offset);
 	}
 
+	__acpi_unmap_table(virt_addr, length);
 	return 0;
 }
 
Index: linux-2.6.git/drivers/acpi/osl.c
===================================================================
--- linux-2.6.git.orig/drivers/acpi/osl.c	2008-01-08 03:31:31.000000000 -0800
+++ linux-2.6.git/drivers/acpi/osl.c	2008-01-08 03:43:46.000000000 -0800
@@ -231,6 +231,8 @@
 {
 	if (acpi_gbl_permanent_mmap) {
 		iounmap(virt);
+	} else {
+		__acpi_unmap_table(virt, size);
 	}
 }
 EXPORT_SYMBOL_GPL(acpi_os_unmap_memory);
Index: linux-2.6.git/include/linux/acpi.h
===================================================================
--- linux-2.6.git.orig/include/linux/acpi.h	2008-01-08 03:31:38.000000000 -0800
+++ linux-2.6.git/include/linux/acpi.h	2008-01-08 03:43:46.000000000 -0800
@@ -79,6 +79,7 @@
 typedef int (*acpi_table_entry_handler) (struct acpi_subtable_header *header, const unsigned long end);
 
 char * __acpi_map_table (unsigned long phys_addr, unsigned long size);
+void __acpi_unmap_table (void * addr, unsigned long size);
 unsigned long acpi_find_rsdp (void);
 int acpi_boot_init (void);
 int acpi_boot_table_init (void);
Index: linux-2.6.git/arch/x86/kernel/mpparse_64.c
===================================================================
--- linux-2.6.git.orig/arch/x86/kernel/mpparse_64.c	2008-01-08 03:31:31.000000000 -0800
+++ linux-2.6.git/arch/x86/kernel/mpparse_64.c	2008-01-08 03:43:46.000000000 -0800
@@ -29,6 +29,7 @@
 #include <asm/io_apic.h>
 #include <asm/proto.h>
 #include <asm/acpi.h>
+#include <asm/bios_ebda.h>
 
 /* Have we found an MP table */
 int smp_found_config;
@@ -535,9 +536,12 @@
 static int __init smp_scan_config (unsigned long base, unsigned long length)
 {
 	extern void __bad_mpf_size(void); 
-	unsigned int *bp = phys_to_virt(base);
+	unsigned int *bp = (unsigned int *)__acpi_map_table(base, length);
 	struct intel_mp_floating *mpf;
 
+	if (!bp)
+		return 0;
+
 	Dprintk("Scan SMP from %p for %ld bytes.\n", bp,length);
 	if (sizeof(*mpf) != 16)
 		__bad_mpf_size();
@@ -555,11 +559,13 @@
 			if (mpf->mpf_physptr)
 				reserve_bootmem_generic(mpf->mpf_physptr, PAGE_SIZE);
 			mpf_found = mpf;
+			__acpi_unmap_table((char *)bp, length);
 			return 1;
 		}
 		bp += 4;
 		length -= 16;
 	}
+	__acpi_unmap_table((char *)bp, length);
 	return 0;
 }
 
@@ -592,11 +598,11 @@
 	 * should be fixed.
 	 */
 
-	address = *(unsigned short *)phys_to_virt(0x40E);
-	address <<= 4;
-	if (smp_scan_config(address, 0x1000))
+	address = get_bios_ebda();
+	if (address && smp_scan_config(address, 0x1000))
 		return;
 
+
 	/* If we have come this far, we did not find an MP table  */
 	 printk(KERN_INFO "No mptable found.\n");
 }
Index: linux-2.6.git/arch/x86/mm/init_64.c
===================================================================
--- linux-2.6.git.orig/arch/x86/mm/init_64.c	2008-01-08 03:41:30.000000000 -0800
+++ linux-2.6.git/arch/x86/mm/init_64.c	2008-01-08 03:43:46.000000000 -0800
@@ -208,7 +208,7 @@
 } 
 
 /* Must run before zap_low_mappings */
-__meminit void *early_ioremap(unsigned long addr, unsigned long size)
+void *early_ioremap(unsigned long addr, unsigned long size)
 {
 	unsigned long vaddr;
 	pmd_t *pmd, *last_pmd;
@@ -237,7 +237,7 @@
 }
 
 /* To avoid virtual aliases later */
-__meminit void early_iounmap(void *addr, unsigned long size)
+void early_iounmap(void *addr, unsigned long size)
 {
 	unsigned long vaddr;
 	pmd_t *pmd;
Index: linux-2.6.git/include/asm-x86/rio.h
===================================================================
--- linux-2.6.git.orig/include/asm-x86/rio.h	2008-01-08 03:41:30.000000000 -0800
+++ linux-2.6.git/include/asm-x86/rio.h	2008-01-08 03:43:46.000000000 -0800
@@ -8,6 +8,8 @@
 #ifndef __ASM_RIO_H
 #define __ASM_RIO_H
 
+#include <asm/bios_ebda.h>
+
 #define RIO_TABLE_VERSION	3
 
 struct rio_table_hdr {
@@ -60,15 +62,4 @@
 	ALT_CALGARY	= 5,  /* Second Planar Calgary      */
 };
 
-/*
- * there is a real-mode segmented pointer pointing to the
- * 4K EBDA area at 0x40E.
- */
-static inline unsigned long get_bios_ebda(void)
-{
-	unsigned long address = *(unsigned short *)phys_to_virt(0x40EUL);
-	address <<= 4;
-	return address;
-}
-
 #endif /* __ASM_RIO_H */
Index: linux-2.6.git/arch/x86/kernel/mpparse_32.c
===================================================================
--- linux-2.6.git.orig/arch/x86/kernel/mpparse_32.c	2008-01-08 03:41:30.000000000 -0800
+++ linux-2.6.git/arch/x86/kernel/mpparse_32.c	2008-01-08 03:43:46.000000000 -0800
@@ -27,11 +27,11 @@
 #include <asm/mtrr.h>
 #include <asm/mpspec.h>
 #include <asm/io_apic.h>
+#include <asm/bios_ebda.h>
 
 #include <mach_apic.h>
 #include <mach_apicdef.h>
 #include <mach_mpparse.h>
-#include <bios_ebda.h>
 
 /* Have we found an MP table */
 int smp_found_config;
@@ -718,9 +718,12 @@
 
 static int __init smp_scan_config (unsigned long base, unsigned long length)
 {
-	unsigned long *bp = phys_to_virt(base);
+	unsigned long *bp = (unsigned long *)__acpi_map_table(base, length);
 	struct intel_mp_floating *mpf;
 
+	if (!bp)
+		return 0;
+
 	Dprintk("Scan SMP from %p for %ld bytes.\n", bp,length);
 	if (sizeof(*mpf) != 16)
 		printk("Error: MPF size\n");
@@ -755,11 +758,13 @@
 			}
 
 			mpf_found = mpf;
+			__acpi_unmap_table((char *)bp, length);
 			return 1;
 		}
 		bp += 4;
 		length -= 16;
 	}
+	__acpi_unmap_table((char *)bp, length);
 	return 0;
 }
 
Index: linux-2.6.git/arch/x86/kernel/pci-calgary_64.c
===================================================================
--- linux-2.6.git.orig/arch/x86/kernel/pci-calgary_64.c	2008-01-08 03:41:30.000000000 -0800
+++ linux-2.6.git/arch/x86/kernel/pci-calgary_64.c	2008-01-08 03:43:46.000000000 -0800
@@ -1180,6 +1180,7 @@
 		}
 	}
 
+	early_iounmap((void *)rio_table_hdr, sizeof(struct rio_table_hdr));
 	return 0;
 
 error:
@@ -1188,6 +1189,7 @@
 		if (bus_info[bus].bbar)
 			iounmap(bus_info[bus].bbar);
 
+	early_iounmap((void *)rio_table_hdr, sizeof(struct rio_table_hdr));
 	return ret;
 }
 
@@ -1337,7 +1339,8 @@
 	int bus;
 	void *tbl;
 	int calgary_found = 0;
-	unsigned long ptr;
+	unsigned long addr;
+	unsigned short *ptr;
 	unsigned int offset, prev_offset;
 	int ret;
 
@@ -1356,7 +1359,9 @@
 
 	printk(KERN_DEBUG "Calgary: detecting Calgary via BIOS EBDA area\n");
 
-	ptr = (unsigned long)phys_to_virt(get_bios_ebda());
+	addr = get_bios_ebda();
+	if (!addr)
+		return;
 
 	rio_table_hdr = NULL;
 	prev_offset = 0;
@@ -1366,14 +1371,22 @@
 	 * Only parse up until the offset increases:
 	 */
 	while (offset > prev_offset) {
+		ptr = early_ioremap(addr + offset, 4);
+		if (!ptr)
+			break;
+
 		/* The block id is stored in the 2nd word */
-		if (*((unsigned short *)(ptr + offset + 2)) == 0x4752){
+		if (ptr[1] == 0x4752){
+			early_iounmap(ptr, 4);
 			/* set the pointer past the offset & block id */
-			rio_table_hdr = (struct rio_table_hdr *)(ptr + offset + 4);
+			ptr = early_ioremap(addr + offset + 4,
+			              sizeof(struct rio_table_hdr));
+			rio_table_hdr = (struct rio_table_hdr *)ptr;
 			break;
 		}
 		prev_offset = offset;
-		offset = *((unsigned short *)(ptr + offset));
+		offset = ptr[0];
+		early_iounmap(ptr, 4);
 	}
 	if (!rio_table_hdr) {
 		printk(KERN_DEBUG "Calgary: Unable to locate Rio Grande table "
@@ -1384,6 +1397,8 @@
 	ret = build_detail_arrays();
 	if (ret) {
 		printk(KERN_DEBUG "Calgary: build_detail_arrays ret %d\n", ret);
+		early_iounmap((void *)rio_table_hdr,
+		              sizeof(struct rio_table_hdr));
 		return;
 	}
 
@@ -1423,6 +1438,10 @@
 		printk(KERN_INFO "PCI-DMA: Calgary TCE table spec is %d, "
 		       "CONFIG_IOMMU_DEBUG is %s.\n", specified_table_size,
 		       debugging ? "enabled" : "disabled");
+		/* rio_table_hdr will be unmapped in calgary_locate_bbars() */
+	} else {
+		early_iounmap((void *)rio_table_hdr,
+		              sizeof(struct rio_table_hdr));
 	}
 	return;
 
@@ -1433,6 +1452,7 @@
 		if (info->tce_space)
 			free_tce_table(info->tce_space);
 	}
+	early_iounmap((void *)rio_table_hdr, sizeof(struct rio_table_hdr));
 }
 
 int __init calgary_iommu_init(void)
Index: linux-2.6.git/arch/x86/kernel/setup_32.c
===================================================================
--- linux-2.6.git.orig/arch/x86/kernel/setup_32.c	2008-01-08 03:41:30.000000000 -0800
+++ linux-2.6.git/arch/x86/kernel/setup_32.c	2008-01-08 03:43:46.000000000 -0800
@@ -61,7 +61,7 @@
 #include <asm/io.h>
 #include <asm/vmi.h>
 #include <setup_arch.h>
-#include <bios_ebda.h>
+#include <asm/bios_ebda.h>
 #include <asm/cacheflush.h>
 
 /* This value is set up by the early boot code to point to the value
Index: linux-2.6.git/arch/x86/kernel/setup_64.c
===================================================================
--- linux-2.6.git.orig/arch/x86/kernel/setup_64.c	2008-01-08 03:41:30.000000000 -0800
+++ linux-2.6.git/arch/x86/kernel/setup_64.c	2008-01-08 03:47:57.000000000 -0800
@@ -62,6 +62,7 @@
 #include <asm/sections.h>
 #include <asm/dmi.h>
 #include <asm/cacheflush.h>
+#include <asm/bios_ebda.h>
 #include <asm/mce.h>
 #include <asm/ds.h>
 
@@ -243,31 +244,36 @@
 {}
 #endif
 
-#define EBDA_ADDR_POINTER 0x40E
-
 unsigned __initdata ebda_addr;
 unsigned __initdata ebda_size;
 
 static void discover_ebda(void)
 {
+	unsigned short *ptr;
 	/*
 	 * there is a real-mode segmented pointer pointing to the
 	 * 4K EBDA area at 0x40E
 	 */
-	ebda_addr = *(unsigned short *)__va(EBDA_ADDR_POINTER);
 	/*
 	 * There can be some situations, like paravirtualized guests,
 	 * in which there is no available ebda information. In such
 	 * case, just skip it
 	 */
+
+	ebda_addr = get_bios_ebda();
 	if (!ebda_addr) {
 		ebda_size = 0;
 		return;
 	}
 
-	ebda_addr <<= 4;
-
-	ebda_size = *(unsigned short *)__va(ebda_addr);
+	ptr = (unsigned short *)__acpi_map_table(ebda_addr, 2);
+	if (!ptr) {
+		ebda_addr = 0;
+		ebda_size = 0;
+		return;
+	}
+	ebda_size = *(unsigned short *)ptr;
+	__acpi_unmap_table((char *)ptr, 2);
 
 	/* Round EBDA up to pages */
 	if (ebda_size == 0)
Index: linux-2.6.git/include/asm-x86/bios_ebda.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.git/include/asm-x86/bios_ebda.h	2008-01-08 03:43:46.000000000 -0800
@@ -0,0 +1,26 @@
+
+#ifndef _BIOS_EBDA_H
+#define _BIOS_EBDA_H
+
+#include <linux/acpi.h>
+
+/*
+ * there is a real-mode segmented pointer pointing to the
+ * 4K EBDA area at 0x40E.
+ */
+static inline unsigned long get_bios_ebda(void)
+{
+	unsigned short	*bp;
+	unsigned long address;
+	bp = (unsigned short *)__acpi_map_table(0x40EUL, 2);
+	if (!bp)
+		return 0;
+
+	address = *bp;
+	address <<= 4;
+	__acpi_unmap_table((char *)bp, 2);
+
+	return address;
+}
+
+#endif /* _MACH_BIOS_EBDA_H */
Index: linux-2.6.git/include/asm-x86/mach-default/bios_ebda.h
===================================================================
--- linux-2.6.git.orig/include/asm-x86/mach-default/bios_ebda.h	2008-01-08 03:31:38.000000000 -0800
+++ /dev/null	1970-01-01 00:00:00.000000000 +0000
@@ -1,15 +0,0 @@
-#ifndef _MACH_BIOS_EBDA_H
-#define _MACH_BIOS_EBDA_H
-
-/*
- * there is a real-mode segmented pointer pointing to the
- * 4K EBDA area at 0x40E.
- */
-static inline unsigned int get_bios_ebda(void)
-{
-	unsigned int address = *(unsigned short *)phys_to_virt(0x40E);
-	address <<= 4;
-	return address;	/* 0 means none */
-}
-
-#endif /* _MACH_BIOS_EBDA_H */

-- 

  reply	other threads:[~2008-01-10 18:55 UTC|newest]

Thread overview: 48+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-01-10 18:48 [patch 00/11] PAT x86: PAT support for x86 venkatesh.pallipadi
2008-01-10 18:48 ` venkatesh.pallipadi [this message]
2008-01-10 18:48 ` [patch 02/11] PAT x86: Map only usable memory in x86_64 identity map and kernel text venkatesh.pallipadi
2008-01-10 19:06   ` Andi Kleen
2008-01-10 19:17     ` Pallipadi, Venkatesh
2008-01-10 19:28       ` Andi Kleen
2008-01-10 20:50         ` Pallipadi, Venkatesh
2008-01-10 21:16           ` Andi Kleen
2008-01-10 22:25             ` Pallipadi, Venkatesh
2008-01-10 22:35               ` Andi Kleen
2008-01-14 16:43           ` Ingo Molnar
2008-01-14 21:21             ` Siddha, Suresh B
2008-01-14 21:28               ` Andi Kleen
2008-01-15 22:17               ` Ingo Molnar
2008-01-15 23:11                 ` Andi Kleen
2008-01-15 23:21                 ` Siddha, Suresh B
2008-01-18 12:01                   ` Ingo Molnar
2008-01-18 13:12                     ` Andi Kleen
2008-01-18 16:46                       ` Jesse Barnes
2008-01-18 18:12                         ` Andi Kleen
2008-01-18 19:02                           ` Jesse Barnes
2008-01-19  2:42                             ` Andi Kleen
2008-01-10 21:05   ` Linus Torvalds
2008-01-10 21:57     ` Pallipadi, Venkatesh
2008-01-10 22:15       ` Linus Torvalds
2008-01-10 22:27         ` Pallipadi, Venkatesh
2008-01-10 22:50         ` Valdis.Kletnieks
2008-01-18 18:27           ` Dave Jones
2008-01-18 20:54             ` Ingo Molnar
2008-01-10 18:48 ` [patch 03/11] PAT x86: Map only usable memory in i386 identity map venkatesh.pallipadi
2008-01-10 19:10   ` Andi Kleen
2008-01-10 18:48 ` [patch 04/11] PAT x86: Basic PAT implementation venkatesh.pallipadi
2008-01-10 18:48 ` [patch 05/11] PAT x86: drm driver changes for PAT venkatesh.pallipadi
2008-01-10 18:48 ` [patch 06/11] PAT x86: Refactoring i386 cpa venkatesh.pallipadi
2008-01-10 19:00   ` Andi Kleen
2008-01-14 16:47     ` Ingo Molnar
2008-01-10 18:48 ` [patch 07/11] PAT x86: pat-conflict resolution using linear list venkatesh.pallipadi
2008-01-10 19:13   ` Andi Kleen
2008-01-10 20:08     ` Pallipadi, Venkatesh
2008-01-10 18:48 ` [patch 08/11] PAT x86: pci mmap conlfict patch venkatesh.pallipadi
2008-01-10 18:48 ` [patch 09/11] PAT x86: Add ioremap_wc support venkatesh.pallipadi
2008-01-10 19:08   ` Andi Kleen
2008-01-10 19:25     ` Pallipadi, Venkatesh
2008-01-12  0:18       ` Roland Dreier
2008-01-10 18:48 ` [patch 10/11] PAT x86: Handle /dev/mem mappings venkatesh.pallipadi
2008-01-10 18:48 ` [patch 11/11] PAT x86: Expose uc and wc interfaces in /sysfs vor pci_mmap_resource venkatesh.pallipadi
2008-01-10 19:43   ` Greg KH
2008-01-10 20:54     ` [patch 11/11] PAT x86: Expose uc and wc interfaces in /sysfsvor pci_mmap_resource Pallipadi, Venkatesh

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20080110184854.557190000@intel.com \
    --to=venkatesh.pallipadi@intel.com \
    --cc=airlied@skynet.ie \
    --cc=ak@muc.de \
    --cc=akpm@linux-foundation.org \
    --cc=arjan@infradead.org \
    --cc=davej@redhat.com \
    --cc=davem@davemloft.net \
    --cc=ebiederm@xmission.com \
    --cc=gregkh@suse.de \
    --cc=hpa@zytor.com \
    --cc=jesse.barnes@intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@elte.hu \
    --cc=rdreier@cisco.com \
    --cc=suresh.b.siddha@intel.com \
    --cc=tglx@linutronix.de \
    --cc=torvalds@linux-foundation.org \
    --subject='Re: [patch 01/11] PAT x86: Make acpi/other drivers map memory instead of assuming identity map' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).