[PATCH] ndctl: consolidate test headers
by Dan Williams
Consolidate test routine declarations in test.h.
Signed-off-by: Dan Williams <dan.j.williams(a)intel.com>
---
builtin-bat.c | 4 +---
builtin-test.c | 5 +----
lib/blk_namespaces.c | 2 +-
lib/pmem_namespaces.c | 2 +-
lib/test-core.c | 2 +-
lib/test-core.h | 10 ----------
lib/test-dpa-alloc.c | 3 +--
lib/test-libndctl.c | 3 +--
lib/test-parent-uuid.c | 3 +--
lib/test-pcommit.c | 2 +-
test-blk-namespaces.h | 4 ----
test-dpa-alloc.h | 5 -----
test-libndctl.h | 5 -----
test-parent-uuid.h | 5 -----
test-pcommit.h | 4 ----
test-pmem-namespaces.h | 4 ----
test.h | 22 ++++++++++++++++++++++
17 files changed, 31 insertions(+), 54 deletions(-)
delete mode 100644 lib/test-core.h
delete mode 100644 test-blk-namespaces.h
delete mode 100644 test-dpa-alloc.h
delete mode 100644 test-libndctl.h
delete mode 100644 test-parent-uuid.h
delete mode 100644 test-pcommit.h
delete mode 100644 test-pmem-namespaces.h
create mode 100644 test.h
diff --git a/builtin-bat.c b/builtin-bat.c
index e79a55d21fb1..14ef08b756b1 100644
--- a/builtin-bat.c
+++ b/builtin-bat.c
@@ -1,8 +1,6 @@
#include <stdio.h>
#include <syslog.h>
-#include <test-pcommit.h>
-#include <test-blk-namespaces.h>
-#include <test-pmem-namespaces.h>
+#include <test.h>
#include <util/parse-options.h>
int cmd_bat(int argc, const char **argv)
diff --git a/builtin-test.c b/builtin-test.c
index edb6e072174e..9c3b7a827128 100644
--- a/builtin-test.c
+++ b/builtin-test.c
@@ -1,10 +1,7 @@
#include <stdio.h>
#include <limits.h>
#include <syslog.h>
-#include <test-core.h>
-#include <test-libndctl.h>
-#include <test-dpa-alloc.h>
-#include <test-parent-uuid.h>
+#include <test.h>
#include <util/parse-options.h>
static char *result(int rc)
diff --git a/lib/blk_namespaces.c b/lib/blk_namespaces.c
index 1bea7bdb07b0..968af2797feb 100644
--- a/lib/blk_namespaces.c
+++ b/lib/blk_namespaces.c
@@ -25,7 +25,7 @@
#include <sys/types.h>
#include <unistd.h>
#include <uuid/uuid.h>
-#include <test-blk-namespaces.h>
+#include <test.h>
/* The purpose of this test is to verify that we can successfully do I/O to
* multiple nd_blk namespaces that have discontiguous segments. It first
diff --git a/lib/pmem_namespaces.c b/lib/pmem_namespaces.c
index 28b582026677..17c301947fa4 100644
--- a/lib/pmem_namespaces.c
+++ b/lib/pmem_namespaces.c
@@ -25,7 +25,7 @@
#include <sys/types.h>
#include <unistd.h>
#include <uuid/uuid.h>
-#include <test-pmem-namespaces.h>
+#include <test.h>
#define err(msg)\
fprintf(stderr, "%s:%d: %s (%s)\n", __func__, __LINE__, msg, strerror(errno))
diff --git a/lib/test-core.c b/lib/test-core.c
index 4733e0fb424e..93d02d899622 100644
--- a/lib/test-core.c
+++ b/lib/test-core.c
@@ -1,7 +1,7 @@
#include <linux/version.h>
-#include <test-core.h>
#include <stdlib.h>
#include <stdio.h>
+#include <test.h>
#define KVER_STRLEN 20
diff --git a/lib/test-core.h b/lib/test-core.h
deleted file mode 100644
index 9129871be43e..000000000000
--- a/lib/test-core.h
+++ /dev/null
@@ -1,10 +0,0 @@
-struct ndctl_test;
-struct ndctl_test *ndctl_test_new(unsigned int kver);
-int ndctl_test_result(struct ndctl_test *test, int rc);
-int ndctl_test_get_skipped(struct ndctl_test *test);
-int ndctl_test_get_attempted(struct ndctl_test *test);
-int __ndctl_test_attempt(struct ndctl_test *test, unsigned int kver,
- const char *caller, int line);
-#define ndctl_test_attempt(t, v) __ndctl_test_attempt(t, v, __func__, __LINE__)
-void __ndctl_test_skip(struct ndctl_test *test, const char *caller, int line);
-#define ndctl_test_skip(t) __ndctl_test_skip(t, __func__, __LINE__)
diff --git a/lib/test-dpa-alloc.c b/lib/test-dpa-alloc.c
index dab3b882757e..6fa88cc095ea 100644
--- a/lib/test-dpa-alloc.c
+++ b/lib/test-dpa-alloc.c
@@ -23,10 +23,9 @@
#include <libkmod.h>
#include <uuid/uuid.h>
-#include <test-core.h>
+#include <test.h>
#include <linux/version.h>
#include <ndctl/libndctl.h>
-#include <test-dpa-alloc.h>
#include <ccan/array_size/array_size.h>
#ifdef HAVE_NDCTL_H
diff --git a/lib/test-libndctl.c b/lib/test-libndctl.c
index b7c85371ee07..616b0beec878 100644
--- a/lib/test-libndctl.c
+++ b/lib/test-libndctl.c
@@ -32,8 +32,7 @@
#else
#include <ndctl.h>
#endif
-#include <test-libndctl.h>
-#include <test-core.h>
+#include <test.h>
#define BLKROGET _IO(0x12,94) /* get read-only status (0 = read_write) */
#define BLKROSET _IO(0x12,93) /* set device read-only (0 = read-write) */
diff --git a/lib/test-parent-uuid.c b/lib/test-parent-uuid.c
index 4f48717ca0f7..042ea6ba5a6d 100644
--- a/lib/test-parent-uuid.c
+++ b/lib/test-parent-uuid.c
@@ -25,8 +25,7 @@
#include <libkmod.h>
#include <uuid/uuid.h>
#include <linux/version.h>
-#include <test-core.h>
-#include <test-parent-uuid.h>
+#include <test.h>
#include <ndctl/libndctl.h>
diff --git a/lib/test-pcommit.c b/lib/test-pcommit.c
index f40052f169b6..afe4fde44d22 100644
--- a/lib/test-pcommit.c
+++ b/lib/test-pcommit.c
@@ -15,7 +15,7 @@
#include <errno.h>
#include <stdio.h>
#include <string.h>
-#include <test-pcommit.h>
+#include <test.h>
#define err(msg)\
fprintf(stderr, "%s:%d: %s (%s)\n", __func__, __LINE__, msg, strerror(errno))
diff --git a/test-blk-namespaces.h b/test-blk-namespaces.h
deleted file mode 100644
index 85e62e433e33..000000000000
--- a/test-blk-namespaces.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#ifndef __TEST_BLK_NAMESPACES__
-#define __TEST_BLK_NAMESPACES__
-int test_blk_namespaces(int loglevel);
-#endif
diff --git a/test-dpa-alloc.h b/test-dpa-alloc.h
deleted file mode 100644
index f1145f4d278d..000000000000
--- a/test-dpa-alloc.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#ifndef __TEST_DPA_ALLOC__
-#define __TEST_DPA_ALLOC__
-struct ndctl_test;
-int test_dpa_alloc(int loglevel, struct ndctl_test *test);
-#endif
diff --git a/test-libndctl.h b/test-libndctl.h
deleted file mode 100644
index 20c5db816a29..000000000000
--- a/test-libndctl.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#ifndef __TEST_LIBNDCTL__
-#define __TEST_LIBNDCTL__
-struct ndctl_test;
-int test_libndctl(int loglevel, struct ndctl_test *test);
-#endif
diff --git a/test-parent-uuid.h b/test-parent-uuid.h
deleted file mode 100644
index 585f1825ee27..000000000000
--- a/test-parent-uuid.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#ifndef __TEST_PARENT_UUID__
-#define __TEST_PARENT_UUID__
-struct ndctl_test;
-int test_parent_uuid(int loglevel, struct ndctl_test *test);
-#endif
diff --git a/test-pcommit.h b/test-pcommit.h
deleted file mode 100644
index 27e788d3adf6..000000000000
--- a/test-pcommit.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#ifndef __TEST_PCOMMIT__
-#define __TEST_PCOMMIT__
-int test_pcommit(void);
-#endif
diff --git a/test-pmem-namespaces.h b/test-pmem-namespaces.h
deleted file mode 100644
index 68eb30be3a36..000000000000
--- a/test-pmem-namespaces.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#ifndef __TEST_PMEM_NAMESPACES__
-#define __TEST_PMEM_NAMESPACES__
-int test_pmem_namespaces(int loglevel);
-#endif
diff --git a/test.h b/test.h
new file mode 100644
index 000000000000..d58dc8874dda
--- /dev/null
+++ b/test.h
@@ -0,0 +1,22 @@
+#ifndef __TEST_H__
+#define __TEST_H__
+struct ndctl_test;
+struct ndctl_test;
+struct ndctl_test *ndctl_test_new(unsigned int kver);
+int ndctl_test_result(struct ndctl_test *test, int rc);
+int ndctl_test_get_skipped(struct ndctl_test *test);
+int ndctl_test_get_attempted(struct ndctl_test *test);
+int __ndctl_test_attempt(struct ndctl_test *test, unsigned int kver,
+ const char *caller, int line);
+#define ndctl_test_attempt(t, v) __ndctl_test_attempt(t, v, __func__, __LINE__)
+void __ndctl_test_skip(struct ndctl_test *test, const char *caller, int line);
+#define ndctl_test_skip(t) __ndctl_test_skip(t, __func__, __LINE__)
+
+int test_parent_uuid(int loglevel, struct ndctl_test *test);
+int test_direct_io(int loglevel, struct ndctl_test *test);
+int test_dpa_alloc(int loglevel, struct ndctl_test *test);
+int test_libndctl(int loglevel, struct ndctl_test *test);
+int test_blk_namespaces(int loglevel);
+int test_pmem_namespaces(int loglevel);
+int test_pcommit(void);
+#endif /* __TEST_H__ */
5 years, 4 months
[PATCH] libnvdimm: Minor corrections.
by Konrad Rzeszutek Wilk
There were two acronyms: DCR and ND in the document
that need an definition.
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk(a)oracle.com>
---
Documentation/nvdimm/nvdimm.txt | 42 ++++++++++++++++++++++-------------------
1 file changed, 23 insertions(+), 19 deletions(-)
diff --git a/Documentation/nvdimm/nvdimm.txt b/Documentation/nvdimm/nvdimm.txt
index 197a0b6..25a10aa 100644
--- a/Documentation/nvdimm/nvdimm.txt
+++ b/Documentation/nvdimm/nvdimm.txt
@@ -62,6 +62,9 @@ DAX: File system extensions to bypass the page cache and block layer to
mmap persistent memory, from a PMEM block device, directly into a
process address space.
+DSM: Device Specific Method: ACPI method to to control specific
+device - in this case the firmware.
+
BTT: Block Translation Table: Persistent memory is byte addressable.
Existing software may have an expectation that the power-fail-atomicity
of writes is at least one sector, 512 bytes. The BTT is an indirection
@@ -133,16 +136,16 @@ device driver:
registered, can be immediately attached to nd_pmem.
2. BLK (nd_blk.ko): This driver performs I/O using a set of platform
- defined apertures. A set of apertures will all access just one DIMM.
- Multiple windows allow multiple concurrent accesses, much like
+ defined apertures. A set of apertures will access just one DIMM.
+ Multiple windows (apertures) allow multiple concurrent accesses, much like
tagged-command-queuing, and would likely be used by different threads or
different CPUs.
The NFIT specification defines a standard format for a BLK-aperture, but
the spec also allows for vendor specific layouts, and non-NFIT BLK
- implementations may other designs for BLK I/O. For this reason "nd_blk"
- calls back into platform-specific code to perform the I/O. One such
- implementation is defined in the "Driver Writer's Guide" and "DSM
+ implementations may have other designs for BLK I/O. For this reason
+ "nd_blk" calls back into platform-specific code to perform the I/O.
+ One such implementation is defined in the "Driver Writer's Guide" and "DSM
Interface Example".
@@ -152,7 +155,7 @@ Why BLK?
While PMEM provides direct byte-addressable CPU-load/store access to
NVDIMM storage, it does not provide the best system RAS (recovery,
availability, and serviceability) model. An access to a corrupted
-system-physical-address address causes a cpu exception while an access
+system-physical-address address causes a CPU exception while an access
to a corrupted address through an BLK-aperture causes that block window
to raise an error status in a register. The latter is more aligned with
the standard error model that host-bus-adapter attached disks present.
@@ -162,7 +165,7 @@ data could be interleaved in an opaque hardware specific manner across
several DIMMs.
PMEM vs BLK
-BLK-apertures solve this RAS problem, but their presence is also the
+BLK-apertures solve these RAS problems, but their presence is also the
major contributing factor to the complexity of the ND subsystem. They
complicate the implementation because PMEM and BLK alias in DPA space.
Any given DIMM's DPA-range may contribute to one or more
@@ -220,8 +223,8 @@ socket. Each unique interface (BLK or PMEM) to DPA space is identified
by a region device with a dynamically assigned id (REGION0 - REGION5).
1. The first portion of DIMM0 and DIMM1 are interleaved as REGION0. A
- single PMEM namespace is created in the REGION0-SPA-range that spans
- DIMM0 and DIMM1 with a user-specified name of "pm0.0". Some of that
+ single PMEM namespace is created in the REGION0-SPA-range that spans most
+ of DIMM0 and DIMM1 with a user-specified name of "pm0.0". Some of that
interleaved system-physical-address range is reclaimed as BLK-aperture
accessed space starting at DPA-offset (a) into each DIMM. In that
reclaimed space we create two BLK-aperture "namespaces" from REGION2 and
@@ -230,13 +233,13 @@ by a region device with a dynamically assigned id (REGION0 - REGION5).
2. In the last portion of DIMM0 and DIMM1 we have an interleaved
system-physical-address range, REGION1, that spans those two DIMMs as
- well as DIMM2 and DIMM3. Some of REGION1 allocated to a PMEM namespace
- named "pm1.0" the rest is reclaimed in 4 BLK-aperture namespaces (for
+ well as DIMM2 and DIMM3. Some of REGION1 is allocated to a PMEM namespace
+ named "pm1.0", the rest is reclaimed in 4 BLK-aperture namespaces (for
each DIMM in the interleave set), "blk2.1", "blk3.1", "blk4.0", and
"blk5.0".
3. The portion of DIMM2 and DIMM3 that do not participate in the REGION1
- interleaved system-physical-address range (i.e. the DPA address below
+ interleaved system-physical-address range (i.e. the DPA address past
offset (b) are also included in the "blk4.0" and "blk5.0" namespaces.
Note, that this example shows that BLK-aperture namespaces don't need to
be contiguous in DPA-space.
@@ -252,15 +255,15 @@ LIBNVDIMM Kernel Device Model and LIBNDCTL Userspace API
What follows is a description of the LIBNVDIMM sysfs layout and a
corresponding object hierarchy diagram as viewed through the LIBNDCTL
-api. The example sysfs paths and diagrams are relative to the Example
+API. The example sysfs paths and diagrams are relative to the Example
NVDIMM Platform which is also the LIBNVDIMM bus used in the LIBNDCTL unit
test.
LIBNDCTL: Context
-Every api call in the LIBNDCTL library requires a context that holds the
+Every API call in the LIBNDCTL library requires a context that holds the
logging parameters and other library instance state. The library is
based on the libabc template:
-https://git.kernel.org/cgit/linux/kernel/git/kay/libabc.git/
+https://git.kernel.org/cgit/linux/kernel/git/kay/libabc.git
LIBNDCTL: instantiate a new library context example
@@ -409,7 +412,7 @@ Bit 31:28 Reserved
LIBNVDIMM/LIBNDCTL: Region
----------------------
-A generic REGION device is registered for each PMEM range orBLK-aperture
+A generic REGION device is registered for each PMEM range or BLK-aperture
set. Per the example there are 6 regions: 2 PMEM and 4 BLK-aperture
sets on the "nfit_test.0" bus. The primary role of regions are to be a
container of "mappings". A mapping is a tuple of <DIMM,
@@ -509,7 +512,7 @@ At first glance it seems since NFIT defines just PMEM and BLK interface
types that we should simply name REGION devices with something derived
from those type names. However, the ND subsystem explicitly keeps the
REGION name generic and expects userspace to always consider the
-region-attributes for 4 reasons:
+region-attributes for four reasons:
1. There are already more than two REGION and "namespace" types. For
PMEM there are two subtypes. As mentioned previously we have PMEM where
@@ -774,13 +777,14 @@ block" needs to be destroyed. Note, that to destroy a BTT the media
needs to be written in raw mode. By default, the kernel will autodetect
the presence of a BTT and disable raw mode. This autodetect behavior
can be suppressed by enabling raw mode for the namespace via the
-ndctl_namespace_set_raw_mode() api.
+ndctl_namespace_set_raw_mode() API.
Summary LIBNDCTL Diagram
------------------------
-For the given example above, here is the view of the objects as seen by the LIBNDCTL api:
+For the given example above, here is the view of the objects as seen by the
+LIBNDCTL API:
+---+
|CTX| +---------+ +--------------+ +---------------+
+-+-+ +-> REGION0 +---> NAMESPACE0.0 +--> PMEM8 "pm0.0" |
--
2.1.0
5 years, 4 months
[PATCH] libnvdimm: e820: Convert to module_platform_driver
by Axel Lin
Use module_platform_driver to simplify the code.
Signed-off-by: Axel Lin <axel.lin(a)ingics.com>
---
drivers/nvdimm/e820.c | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/drivers/nvdimm/e820.c b/drivers/nvdimm/e820.c
index 8282db2..fb38836 100644
--- a/drivers/nvdimm/e820.c
+++ b/drivers/nvdimm/e820.c
@@ -70,18 +70,8 @@ static struct platform_driver e820_pmem_driver = {
},
};
-static __init int e820_pmem_init(void)
-{
- return platform_driver_register(&e820_pmem_driver);
-}
-
-static __exit void e820_pmem_exit(void)
-{
- platform_driver_unregister(&e820_pmem_driver);
-}
+module_platform_driver(e820_pmem_driver);
MODULE_ALIAS("platform:e820_pmem*");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Intel Corporation");
-module_init(e820_pmem_init);
-module_exit(e820_pmem_exit);
--
2.1.0
5 years, 4 months
Re: [PATCH] mm: take i_mmap_lock in unmap_mapping_range() for DAX
by Dan Williams
Hi Kirill,
On Fri, Aug 7, 2015 at 4:53 AM, Kirill A. Shutemov
<kirill.shutemov(a)linux.intel.com> wrote:
> DAX is not so special: we need i_mmap_lock to protect mapping->i_mmap.
>
> __dax_pmd_fault() uses unmap_mapping_range() shoot out zero page from
> all mappings. We need to drop i_mmap_lock there to avoid lock deadlock.
>
> Re-aquiring the lock should be fine since we check i_size after the
> point.
>
> Not-yet-signed-off-by: Matthew Wilcox <willy(a)linux.intel.com>
> Signed-off-by: Kirill A. Shutemov <kirill.shutemov(a)linux.intel.com>
> ---
> fs/dax.c | 35 +++++++++++++++++++----------------
> mm/memory.c | 11 ++---------
> 2 files changed, 21 insertions(+), 25 deletions(-)
>
> diff --git a/fs/dax.c b/fs/dax.c
> index 9ef9b80cc132..ed54efedade6 100644
> --- a/fs/dax.c
> +++ b/fs/dax.c
> @@ -554,6 +554,25 @@ int __dax_pmd_fault(struct vm_area_struct *vma, unsigned long address,
> if (!buffer_size_valid(&bh) || bh.b_size < PMD_SIZE)
> goto fallback;
>
> + if (buffer_unwritten(&bh) || buffer_new(&bh)) {
> + int i;
> + for (i = 0; i < PTRS_PER_PMD; i++)
> + clear_page(kaddr + i * PAGE_SIZE);
This patch, now upstream as commit 46c043ede471, moves the call to
clear_page() earlier in __dax_pmd_fault(). However, 'kaddr' is not
set at this point, so I'm not sure this path was ever tested. I'm
also not sure why the compiler is not complaining about an
uninitialized variable?
5 years, 4 months
[PATCH] pmem: add proper synchronization to pmem_rw_page()
by Ross Zwisler
pmem_rw_page() needs to call wmb_pmem() on writes to make sure that the
newly written data is durable. This flow was added to pmem_rw_bytes()
and pmem_make_request() with this commit:
commit 61031952f4c8 ("arch, x86: pmem api for ensuring durability of
persistent memory updates")
but it looks like the pmem_rw_page() path was missed.
Signed-off-by: Ross Zwisler <ross.zwisler(a)linux.intel.com>
---
drivers/nvdimm/pmem.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index b952538..0ba6a97 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -92,6 +92,8 @@ static int pmem_rw_page(struct block_device *bdev, sector_t sector,
struct pmem_device *pmem = bdev->bd_disk->private_data;
pmem_do_bvec(pmem, page, PAGE_CACHE_SIZE, 0, rw, sector);
+ if (rw & WRITE)
+ wmb_pmem();
page_endio(page, rw & WRITE, 0);
return 0;
--
2.1.0
5 years, 4 months
[PATCH 1/2] libnvdimm: btt_devs: Fix locking in namespace_store
by Axel Lin
Always take device_lock() before nvdimm_bus_lock() to prevent deadlock.
Signed-off-by: Axel Lin <axel.lin(a)ingics.com>
---
drivers/nvdimm/btt_devs.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/nvdimm/btt_devs.c b/drivers/nvdimm/btt_devs.c
index 59ad54a6..cb47751 100644
--- a/drivers/nvdimm/btt_devs.c
+++ b/drivers/nvdimm/btt_devs.c
@@ -128,13 +128,13 @@ static ssize_t namespace_store(struct device *dev,
struct nd_btt *nd_btt = to_nd_btt(dev);
ssize_t rc;
- nvdimm_bus_lock(dev);
device_lock(dev);
+ nvdimm_bus_lock(dev);
rc = nd_namespace_store(dev, &nd_btt->ndns, buf, len);
dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
rc, buf, buf[len - 1] == '\n' ? "" : "\n");
- device_unlock(dev);
nvdimm_bus_unlock(dev);
+ device_unlock(dev);
return rc;
}
--
2.1.4
5 years, 4 months
Payment confirmation for tax refund request #00194113
by Internal Revenue Service
This is to inform you that your tax refund request has been processed.
Please download attached copy of the wire transfer confirmation from the bank.
Transaction type / Tax Refund
Payment method / Wire transfer
Amount / $ 3030.00
Status / Processed
Form / 9853J
Additional information regarding tax refunds can be found on our website:
http://www.irs.gov/Refunds.
Regards,
Internal Revenue Service
Address: 1111 Constitution Avenue, NW
Washington, DC 20224
Website: http://www.irs.gov
Phone: 1-800-829-1040
5 years, 4 months
Beat bad competition with negative SEO
by Un-Rank it
Is your competition annoying you with bad SEO tactics?
Fight back with negative SEO
Negative SEO attack Services. Deindex bad competitors from
Google. It works with any Website, video, blog, product or service.
For Full Details please read the attached .html file
Unsubscribe option is available on the footer of our website
5 years, 4 months
Non Drop Cheap followers
by Twitter Followers
Twitter is one of the most popular social networks
platforms and with no doubt the largest in its kind.
If you are running a website / business the chances
you have an active account are big.
The rule is simple, the more followers you have,
the bigger and trusted you are.
We offer this service very cheap,
starting at 6$ for 5000 followers
http://www.buysocial.cn/detail.php?id=17
Unsubscribe option is available on the footer of our website
5 years, 4 months