[PATCH 2/2] ata: Add support for Long Logical Sectors and Long Physical Sectors

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Matthew Wilcox <willy@xxxxxxxxxxxxxxx>

ATA 8 permits devices that have sector sizes larger than 512 bytes.
We support this by recording the sector size in the ata_device and use
it instead of the ATA_SECT_SIZE when the data transfer is a multiple
of sectors.  Drivers must indicate their support for sector sizes other
than 512 by implementing the 'sector_size_supported' port operation.

Signed-off-by: Matthew Wilcox <willy@xxxxxxxxxxxxxxx>
---
 drivers/ata/Makefile       |   11 ++++
 drivers/ata/ata-commands.c |  132 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/ata/bitops.c       |   77 +++++++++++++++++++++++++
 drivers/ata/bitops.h       |   12 ++++
 drivers/ata/libata-core.c  |   67 ++++++++++++++++++++++
 drivers/ata/libata-scsi.c  |   52 +++++++++++++-----
 include/linux/libata.h     |    3 +
 7 files changed, 340 insertions(+), 14 deletions(-)
 create mode 100644 drivers/ata/ata-commands.c
 create mode 100644 drivers/ata/bitops.c
 create mode 100644 drivers/ata/bitops.h

diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 7f1ecf9..a67d5dc 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -84,3 +84,14 @@ libata-objs	:= libata-core.o libata-scsi.o libata-eh.o
 libata-$(CONFIG_ATA_SFF)	+= libata-sff.o
 libata-$(CONFIG_SATA_PMP)	+= libata-pmp.o
 libata-$(CONFIG_ATA_ACPI)	+= libata-acpi.o
+
+hostprogs-y := ata-commands
+ata-commands-objs := ata-commands.o bitops.o
+HOSTCFLAGS_ata-commands.o       := -Iinclude
+clean-files := all-commands sector-commands
+quiet_cmd_ata-cmds = ATA-CMD 
+      cmd_ata-cmds = $(obj)/ata-commands || (rm -f all-commands sector-commands)
+
+$(obj)/all-commands: $(obj)/ata-commands
+	$(call if_changed,ata-cmds)
+$(obj)/libata-core.o: $(obj)/all-commands
diff --git a/drivers/ata/ata-commands.c b/drivers/ata/ata-commands.c
new file mode 100644
index 0000000..afb40c1
--- /dev/null
+++ b/drivers/ata/ata-commands.c
@@ -0,0 +1,132 @@
+/*
+ * ata-commands.c
+ *
+ * Copyright (c) 2008 Intel Corporation
+ * Author: Matthew Wilcox <willy@xxxxxxxxxxxxxxx>
+ *
+ * This file is part of the Linux kernel, and is made available under
+ * the terms of the GNU General Public License, version 2
+ */
+
+#include <stdio.h>
+
+#include "bitops.h"
+
+/* We have to define some types to include ata.h */
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned long long u64;
+typedef int bool;
+#define false 0
+#define true 1
+#define _LINUX_SWAB_H
+#include "linux/ata.h"
+
+void set_sector_bits(void)
+{
+	set_bit(ATA_CMD_CFA_TRANSLATE_SECTOR);
+	set_bit(ATA_CMD_CFA_WRITE_MULTI_WITHOUT_ERASE);
+	set_bit(ATA_CMD_CFA_WRITE_SECTORS_WITHOUT_ERASE);
+	set_bit(ATA_CMD_READ);
+	set_bit(ATA_CMD_READ_EXT);
+	set_bit(ATA_CMD_READ_QUEUED);
+	set_bit(ATA_CMD_READ_QUEUED_EXT);
+	set_bit(ATA_CMD_FPDMA_READ);
+	set_bit(ATA_CMD_READ_MULTI);
+	set_bit(ATA_CMD_READ_MULTI_EXT);
+	set_bit(ATA_CMD_PIO_READ);
+	set_bit(ATA_CMD_PIO_READ_EXT);
+	set_bit(ATA_CMD_READ_STREAM_DMA_EXT);
+	set_bit(ATA_CMD_READ_STREAM_EXT);
+	set_bit(ATA_CMD_VERIFY);
+	set_bit(ATA_CMD_VERIFY_EXT);
+	set_bit(ATA_CMD_WRITE);
+	set_bit(ATA_CMD_WRITE_EXT);
+	set_bit(ATA_CMD_WRITE_FUA_EXT);
+	set_bit(ATA_CMD_WRITE_DMA_QUEUED);
+	set_bit(ATA_CMD_WRITE_DMA_QUEUED_EXT);
+	set_bit(ATA_CMD_WRITE_DMA_QUEUED_FUA_EXT);
+	set_bit(ATA_CMD_FPDMA_WRITE);
+	set_bit(ATA_CMD_WRITE_MULTI);
+	set_bit(ATA_CMD_WRITE_MULTI_EXT);
+	set_bit(ATA_CMD_WRITE_MULTI_FUA_EXT);
+	set_bit(ATA_CMD_PIO_WRITE);
+	set_bit(ATA_CMD_PIO_WRITE_EXT);
+	set_bit(ATA_CMD_WRITE_STREAM_DMA_EXT);
+	set_bit(ATA_CMD_WRITE_STREAM_EXT);
+}
+
+void set_512_bits(void)
+{
+	set_bit(ATA_CMD_CFA_ERASE_SECTORS);
+	set_bit(ATA_CMD_CFA_REQUEST_EXT_ERROR);
+	set_bit(ATA_CMD_CHK_MEDIA_CARD_TYPE);
+	set_bit(ATA_CMD_CHK_POWER);
+	set_bit(ATA_CMD_CONFIG_STREAM);
+	set_bit(ATA_CMD_CONF_OVERLAY);
+	set_bit(ATA_CMD_DEV_RESET);
+	set_bit(ATA_CMD_DLOAD_MCODE);
+	set_bit(ATA_CMD_EDD);
+	set_bit(ATA_CMD_FLUSH);
+	set_bit(ATA_CMD_FLUSH_EXT);
+	set_bit(ATA_CMD_ID_ATA);
+	set_bit(ATA_CMD_ID_ATAPI);
+	set_bit(ATA_CMD_IDLE);
+	set_bit(ATA_CMD_IDLEIMMEDIATE);
+	set_bit(ATA_CMD_MEDIA_LOCK);
+	set_bit(ATA_CMD_MEDIA_UNLOCK);
+	set_bit(ATA_CMD_NVCACHE);
+	set_bit(ATA_CMD_NOP);
+	set_bit(ATA_CMD_PACKET);
+	set_bit(ATA_CMD_PMP_READ);
+	set_bit(ATA_CMD_READ_LOG_EXT);
+	set_bit(ATA_CMD_READ_LOG_DMA_EXT);
+	set_bit(ATA_CMD_READ_NATIVE_MAX);
+	set_bit(ATA_CMD_READ_NATIVE_MAX_EXT);
+	set_bit(ATA_CMD_RESTORE);
+	set_bit(ATA_CMD_SEC_DISABLE_PASSWORD);
+	set_bit(ATA_CMD_SEC_ERASE_PREPARE);
+	set_bit(ATA_CMD_SEC_ERASE_UNIT);
+	set_bit(ATA_CMD_SEC_FREEZE_LOCK);
+	set_bit(ATA_CMD_SEC_SET_PASSWORD);
+	set_bit(ATA_CMD_SEC_UNLOCK);
+	set_bit(ATA_CMD_SERVICE);
+	set_bit(ATA_CMD_SET_FEATURES);
+	set_bit(ATA_CMD_SET_MAX);
+	set_bit(ATA_CMD_SET_MAX_EXT);
+	set_bit(ATA_CMD_SET_MULTI);
+	set_bit(ATA_CMD_SLEEP);
+	set_bit(ATA_CMD_SMART);
+	set_bit(ATA_CMD_STANDBY);
+	set_bit(ATA_CMD_STANDBYNOW1);
+	set_bit(ATA_CMD_TRUSTED_NON_DATA);
+	set_bit(ATA_CMD_TRUSTED_RECEIVE);
+	set_bit(ATA_CMD_TRUSTED_RECEIVE_DMA);
+	set_bit(ATA_CMD_TRUSTED_SEND);
+	set_bit(ATA_CMD_TRUSTED_SEND_DMA);
+	set_bit(ATA_CMD_PMP_WRITE);
+	set_bit(ATA_CMD_WRITE_LOG_EXT);
+	set_bit(ATA_CMD_WRITE_LOG_DMA_EXT);
+	set_bit(ATA_CMD_WRITE_UNCORRECTABLE_EXT);
+	set_bit(ATA_CMD_INIT_DEV_PARAMS);
+	set_bit(ATA_EXABYTE_ENABLE_NEST);
+}
+
+int main(int argc, char **argv)
+{
+	FILE *out;
+
+	set_sector_bits();
+	out = fopen("drivers/ata/sector-commands", "w");
+	output_array(out);
+	fclose(out);
+
+	set_512_bits();
+	out = fopen("drivers/ata/all-commands", "w");
+	output_array(out);
+	fclose(out);
+
+	return 0;
+}
diff --git a/drivers/ata/bitops.c b/drivers/ata/bitops.c
new file mode 100644
index 0000000..f6a6f06
--- /dev/null
+++ b/drivers/ata/bitops.c
@@ -0,0 +1,77 @@
+/*
+ * bitops.c
+ *
+ * Copyright 2008 Intel Corporation
+ * Author: Matthew Wilcox <willy@xxxxxxxxxxxxxxx>
+ *
+ * This file is part of the Linux kernel, and is made available under
+ * the terms of the GNU General Public License, version 2
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bitops.h"
+
+static unsigned int *array;
+static unsigned maxbit, allocbit;
+
+static void expand_array(unsigned bit)
+{
+	unsigned size;
+	void *tmp;
+
+	if (bit > maxbit)
+		maxbit = bit;
+	if (bit < allocbit)
+		return;
+
+	size = ((bit + 63) / 64) * 8;
+	tmp = realloc(array, size);
+	if (!tmp) {
+		fprintf(stderr, "Memory allocation failure\n");
+		exit(1);
+	}
+	array = tmp;
+	memset(array + allocbit / 8, 0, size - allocbit / 8);
+	allocbit = size * 8;
+}
+
+void set_bit(unsigned bit)
+{
+	expand_array(bit);
+	array[bit / 32] |= 1 << (bit % 32);
+}
+
+static const char *sep(unsigned i, unsigned w)
+{
+	if (i >= w - 1)
+		return "\n";
+	if (i % 6 == 5)
+		return "\n\t";
+	return " ";
+}
+
+void output_array(FILE *file)
+{
+	unsigned i, words = (maxbit + 32) / 32;
+
+	fprintf(file, "{\n#ifdef CONFIG_64BIT\n\t");
+	for (i = 0; i < words; i += 2) {
+		fprintf(file, "0x%08x%08xULL,%s", array[i + 1], array[i],
+							sep(i + 1, words));
+	}
+	fprintf(file, "#else\n\t");
+	for (i = 0; i < words; i++) {
+		fprintf(file, "0x%08x,%s", array[i], sep(i, words));
+	}
+	fprintf(file, "#endif\n};\n");
+}
+
+void free_array(void)
+{
+	free(array);
+	array = NULL;
+	maxbit = allocbit = 0;
+}
diff --git a/drivers/ata/bitops.h b/drivers/ata/bitops.h
new file mode 100644
index 0000000..3da560c
--- /dev/null
+++ b/drivers/ata/bitops.h
@@ -0,0 +1,12 @@
+/*
+ * A library for creating statically initialised bit arrays
+ */
+
+/* Call this to set a bit. */
+extern void set_bit(unsigned bit);
+
+/* Call this to output the array. */
+extern void output_array(FILE *file);
+
+/* Remove all bits from the array. */
+extern void free_array(void);
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 9fbf059..61378d8 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -600,6 +600,32 @@ void ata_tf_from_fis(const u8 *fis, struct ata_taskfile *tf)
 	tf->hob_nsect	= fis[13];
 }
 
+/**
+ * ata_sect_size - Returns the sector size to use for a command
+ * @command: The ATA command byte
+ * @dev_sect_size: The size of the drive's sectors
+ *
+ * Some commands are specified to transfer (a multiple of) 512 bytes of data
+ * while others transfer a multiple of the number of bytes in a sector.  This
+ * function knows which commands transfer how much data.
+ */
+unsigned ata_sect_size(u8 command, unsigned dev_sect_size)
+{
+	static const unsigned long sector_commands[] =
+#include "sector-commands"
+	static unsigned long known_commands[] =
+#include "all-commands"
+
+	if (test_bit(command, sector_commands))
+		return dev_sect_size;
+	if (!test_bit(command, known_commands)) {
+		printk(KERN_ERR "Unknown ata cmd %d, assuming "
+				"512 byte sector size\n", command);
+		set_bit(command, known_commands);
+	}
+	return 512;
+}
+
 static const u8 ata_rw_cmds[] = {
 	/* pio multi */
 	ATA_CMD_READ_MULTI,
@@ -1333,6 +1359,25 @@ static u64 ata_id_n_sectors(const u16 *id)
 	}
 }
 
+/*
+ * ATA supports sector sizes up to 2^33 - 1.  The reported sector size may
+ * not be a power of two.  The extra bytes are used for user-visible data
+ * integrity calculations.  Note this is not the same as the ECC which is
+ * accessed through the SCT Command Transport or READ / WRITE LONG.
+ */
+static u64 ata_id_sect_size(const u16 *id)
+{
+	u16 word_106 = id[106];
+	u64 sz;
+
+	if ((word_106 & 0xc000) != 0x4000)
+		return ATA_SECT_SIZE;
+	if (!(word_106 & (1 << 12)))
+		return ATA_SECT_SIZE;
+	sz = (id[117] | ((u64)id[118] << 16)) * 2;
+	return sz;
+}
+
 u64 ata_tf_to_lba48(const struct ata_taskfile *tf)
 {
 	u64 sectors = 0;
@@ -2301,6 +2346,20 @@ static void ata_dev_config_ncq(struct ata_device *dev,
 		snprintf(desc, desc_sz, "NCQ (depth %d/%d)", hdepth, ddepth);
 }
 
+static int ata_check_sect_size(struct ata_device *dev)
+{
+	/* Every host can handle 512 byte sectors */
+	if (dev->sect_size == 512)
+		return 0;
+	/* Linux doesn't handle sectors larger than 4GB.  This may be
+	 * a problem around 2050 or so.  Deal with it then.  */
+	if (dev->sect_size > 0xffffffffULL)
+		return -EINVAL;
+	if (!dev->link->ap->ops->sector_size_supported)
+		return -EINVAL;
+	return dev->link->ap->ops->sector_size_supported(dev) ? 0 : -EINVAL;
+}
+
 /**
  *	ata_dev_configure - Configure the specified ATA/ATAPI device
  *	@dev: Target device to configure
@@ -2384,6 +2443,7 @@ int ata_dev_configure(struct ata_device *dev)
 	dev->max_sectors = 0;
 	dev->cdb_len = 0;
 	dev->n_sectors = 0;
+	dev->sect_size = ATA_SECT_SIZE;
 	dev->cylinders = 0;
 	dev->heads = 0;
 	dev->sectors = 0;
@@ -2423,6 +2483,13 @@ int ata_dev_configure(struct ata_device *dev)
 		}
 
 		dev->n_sectors = ata_id_n_sectors(id);
+		dev->sect_size = ata_id_sect_size(id);
+		rc = ata_check_sect_size(dev);
+		if (rc) {
+			ata_dev_printk(dev, KERN_ERR, "sector size %lld not "
+					"supported.\n", dev->sect_size);
+			goto err_out_nosup;
+		}
 
 		if (dev->id[59] & 0x100)
 			dev->multi_count = dev->id[59] & 0xff;
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index b9747fa..9e89601 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -50,7 +50,6 @@
 
 #include "libata.h"
 
-#define SECTOR_SIZE		512
 #define ATA_SCSI_RBUF_SIZE	4096
 
 static DEFINE_SPINLOCK(ata_scsi_rbuf_lock);
@@ -455,7 +454,7 @@ static int ata_get_identity(struct ata_port *ap, struct scsi_device *sdev,
 
 /**
  *	ata_cmd_ioctl - Handler for HDIO_DRIVE_CMD ioctl
- *	@scsidev: Device to which we are issuing command
+ *	@sdev: Device to which we are issuing command
  *	@arg: User provided data for issuing command
  *
  *	LOCKING:
@@ -464,7 +463,7 @@ static int ata_get_identity(struct ata_port *ap, struct scsi_device *sdev,
  *	RETURNS:
  *	Zero on success, negative errno on error.
  */
-int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg)
+int ata_cmd_ioctl(struct scsi_device *sdev, void __user *arg)
 {
 	int rc = 0;
 	u8 scsi_cmd[MAX_COMMAND_SIZE];
@@ -486,7 +485,8 @@ int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg)
 	memset(scsi_cmd, 0, sizeof(scsi_cmd));
 
 	if (args[3]) {
-		argsize = SECTOR_SIZE * args[3];
+		unsigned sect_size = ata_sect_size(args[0], sdev->sector_size);
+		argsize = sect_size * args[3];
 		argbuf = kmalloc(argsize, GFP_KERNEL);
 		if (argbuf == NULL) {
 			rc = -ENOMEM;
@@ -518,7 +518,7 @@ int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg)
 
 	/* Good values for timeout and retries?  Values below
 	   from scsi_ioctl_send_command() for default case... */
-	cmd_result = scsi_execute(scsidev, scsi_cmd, data_dir, argbuf, argsize,
+	cmd_result = scsi_execute(sdev, scsi_cmd, data_dir, argbuf, argsize,
 				  sensebuf, (10*HZ), 5, 0, NULL);
 
 	if (driver_byte(cmd_result) == DRIVER_SENSE) {/* sense data available */
@@ -1630,6 +1630,7 @@ nothing_to_do:
 static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
 {
 	struct scsi_cmnd *scmd = qc->scsicmd;
+	struct ata_device *dev = qc->dev;
 	const u8 *cdb = scmd->cmnd;
 	unsigned int tf_flags = 0;
 	u64 block;
@@ -1686,9 +1687,9 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
 		goto nothing_to_do;
 
 	qc->flags |= ATA_QCFLAG_IO;
-	qc->nbytes = n_block * ATA_SECT_SIZE;
+	qc->nbytes = n_block * dev->sect_size;
 
-	rc = ata_build_rw_tf(&qc->tf, qc->dev, block, n_block, tf_flags,
+	rc = ata_build_rw_tf(&qc->tf, dev, block, n_block, tf_flags,
 			     qc->tag);
 	if (likely(rc == 0))
 		return 0;
@@ -2354,10 +2355,25 @@ saving_not_supp:
  */
 static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
 {
-	u64 last_lba = args->dev->n_sectors - 1; /* LBA of the last block */
+	struct ata_device *dev = args->dev;
+	u64 last_lba = dev->n_sectors - 1; /* LBA of the last block */
+	u32 sector_size;
+	u8 log_per_phys = 1;
+	u16 first_sector_offset = 0;
+	u16 word_106 = dev->id[106];
 
 	VPRINTK("ENTER\n");
 
+	if ((word_106 & 0xc000) == 0x4000) {
+		/* Number and offset of logical sectors per physical sector */
+		if (word_106 & (1 << 13))
+			log_per_phys = word_106 & 0xf;
+		if ((dev->id[209] & 0xc000) == 0x4000)
+			first_sector_offset = dev->id[209] & 0x3fff;
+	}
+
+	sector_size = dev->sect_size;
+
 	if (args->cmd->cmnd[0] == READ_CAPACITY) {
 		if (last_lba >= 0xffffffffULL)
 			last_lba = 0xffffffff;
@@ -2368,9 +2384,10 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
 		rbuf[2] = last_lba >> (8 * 1);
 		rbuf[3] = last_lba;
 
-		/* sector size */
-		rbuf[6] = ATA_SECT_SIZE >> 8;
-		rbuf[7] = ATA_SECT_SIZE & 0xff;
+		rbuf[4] = sector_size >> (8 * 3);
+		rbuf[5] = sector_size >> (8 * 2);
+		rbuf[6] = sector_size >> (8 * 1);
+		rbuf[7] = sector_size;
 	} else {
 		/* sector count, 64-bit */
 		rbuf[0] = last_lba >> (8 * 7);
@@ -2383,8 +2400,15 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf)
 		rbuf[7] = last_lba;
 
 		/* sector size */
-		rbuf[10] = ATA_SECT_SIZE >> 8;
-		rbuf[11] = ATA_SECT_SIZE & 0xff;
+		rbuf[8] = sector_size >> (8 * 3);
+		rbuf[9] = sector_size >> (8 * 2);
+		rbuf[10] = sector_size >> (8 * 1);
+		rbuf[11] = sector_size;
+
+		rbuf[12] = 0;
+		rbuf[13] = log_per_phys;
+		rbuf[14] = first_sector_offset >> 8;
+		rbuf[15] = first_sector_offset;
 	}
 
 	return 0;
@@ -2858,7 +2882,7 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
 	}
 
 	/* READ/WRITE LONG use a non-standard sect_size */
-	qc->sect_size = ATA_SECT_SIZE;
+	qc->sect_size = ata_sect_size(tf->command, dev->sect_size);
 	switch (tf->command) {
 	case ATA_CMD_READ_LONG:
 	case ATA_CMD_READ_LONG_ONCE:
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 5d87bc0..e0d17ec 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -583,6 +583,7 @@ struct ata_device {
 #endif
 	/* n_sector is CLEAR_BEGIN, read comment above CLEAR_BEGIN */
 	u64			n_sectors;	/* size of device, if ATA */
+	u64			sect_size;	/* Logical, not physical */
 	unsigned int		class;		/* ATA_DEV_xxx */
 	unsigned long		unpark_deadline;
 
@@ -781,6 +782,7 @@ struct ata_port_operations {
 	unsigned int (*read_id)(struct ata_device *dev, struct ata_taskfile *tf, u16 *id);
 
 	void (*dev_config)(struct ata_device *dev);
+	bool (*sector_size_supported)(struct ata_device *dev);
 
 	void (*freeze)(struct ata_port *ap);
 	void (*thaw)(struct ata_port *ap);
@@ -988,6 +990,7 @@ extern unsigned int ata_do_dev_read_id(struct ata_device *dev,
 					struct ata_taskfile *tf, u16 *id);
 extern void ata_qc_complete(struct ata_queued_cmd *qc);
 extern int ata_qc_complete_multiple(struct ata_port *ap, u32 qc_active);
+extern unsigned ata_sect_size(u8 command, unsigned dev_sect_size);
 extern void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd,
 			      void (*done)(struct scsi_cmnd *));
 extern int ata_std_bios_param(struct scsi_device *sdev,
-- 
1.5.6.5

--
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Filesystems]     [Linux SCSI]     [Linux RAID]     [Git]     [Kernel Newbies]     [Linux Newbie]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Samba]     [Device Mapper]

  Powered by Linux