mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 06:00:52 +07:00
mm, dax, pmem: introduce pfn_t
For the purpose of communicating the optional presence of a 'struct page' for the pfn returned from ->direct_access(), introduce a type that encapsulates a page-frame-number plus flags. These flags contain the historical "page_link" encoding for a scatterlist entry, but can also denote "device memory". Where "device memory" is a set of pfns that are not part of the kernel's linear mapping by default, but are accessed via the same memory controller as ram. The motivation for this new type is large capacity persistent memory that needs struct page entries in the 'memmap' to support 3rd party DMA (i.e. O_DIRECT I/O with a persistent memory source/target). However, we also need it in support of maintaining a list of mapped inodes which need to be unmapped at driver teardown or freeze_bdev() time. Signed-off-by: Dan Williams <dan.j.williams@intel.com> Cc: Christoph Hellwig <hch@lst.de> Cc: Dave Hansen <dave@sr71.net> Cc: Ross Zwisler <ross.zwisler@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
ba049e93ae
commit
34c0fd540e
@ -43,6 +43,7 @@
|
|||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
|
#include <linux/pfn_t.h>
|
||||||
|
|
||||||
#include <asm/page.h>
|
#include <asm/page.h>
|
||||||
#include <asm/prom.h>
|
#include <asm/prom.h>
|
||||||
@ -142,15 +143,13 @@ axon_ram_make_request(struct request_queue *queue, struct bio *bio)
|
|||||||
*/
|
*/
|
||||||
static long
|
static long
|
||||||
axon_ram_direct_access(struct block_device *device, sector_t sector,
|
axon_ram_direct_access(struct block_device *device, sector_t sector,
|
||||||
void __pmem **kaddr, unsigned long *pfn)
|
void __pmem **kaddr, pfn_t *pfn)
|
||||||
{
|
{
|
||||||
struct axon_ram_bank *bank = device->bd_disk->private_data;
|
struct axon_ram_bank *bank = device->bd_disk->private_data;
|
||||||
loff_t offset = (loff_t)sector << AXON_RAM_SECTOR_SHIFT;
|
loff_t offset = (loff_t)sector << AXON_RAM_SECTOR_SHIFT;
|
||||||
void *addr = (void *)(bank->ph_addr + offset);
|
|
||||||
|
|
||||||
*kaddr = (void __pmem *)addr;
|
|
||||||
*pfn = virt_to_phys(addr) >> PAGE_SHIFT;
|
|
||||||
|
|
||||||
|
*kaddr = (void __pmem __force *) bank->io_addr + offset;
|
||||||
|
*pfn = phys_to_pfn_t(bank->ph_addr + offset, PFN_DEV);
|
||||||
return bank->size - offset;
|
return bank->size - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,9 @@
|
|||||||
#include <linux/radix-tree.h>
|
#include <linux/radix-tree.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#ifdef CONFIG_BLK_DEV_RAM_DAX
|
||||||
|
#include <linux/pfn_t.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
|
|
||||||
@ -378,7 +381,7 @@ static int brd_rw_page(struct block_device *bdev, sector_t sector,
|
|||||||
|
|
||||||
#ifdef CONFIG_BLK_DEV_RAM_DAX
|
#ifdef CONFIG_BLK_DEV_RAM_DAX
|
||||||
static long brd_direct_access(struct block_device *bdev, sector_t sector,
|
static long brd_direct_access(struct block_device *bdev, sector_t sector,
|
||||||
void __pmem **kaddr, unsigned long *pfn)
|
void __pmem **kaddr, pfn_t *pfn)
|
||||||
{
|
{
|
||||||
struct brd_device *brd = bdev->bd_disk->private_data;
|
struct brd_device *brd = bdev->bd_disk->private_data;
|
||||||
struct page *page;
|
struct page *page;
|
||||||
@ -389,7 +392,7 @@ static long brd_direct_access(struct block_device *bdev, sector_t sector,
|
|||||||
if (!page)
|
if (!page)
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
*kaddr = (void __pmem *)page_address(page);
|
*kaddr = (void __pmem *)page_address(page);
|
||||||
*pfn = page_to_pfn(page);
|
*pfn = page_to_pfn_t(page);
|
||||||
|
|
||||||
return PAGE_SIZE;
|
return PAGE_SIZE;
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include <linux/moduleparam.h>
|
#include <linux/moduleparam.h>
|
||||||
#include <linux/badblocks.h>
|
#include <linux/badblocks.h>
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
|
#include <linux/pfn_t.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/pmem.h>
|
#include <linux/pmem.h>
|
||||||
#include <linux/nd.h>
|
#include <linux/nd.h>
|
||||||
@ -40,6 +41,7 @@ struct pmem_device {
|
|||||||
phys_addr_t phys_addr;
|
phys_addr_t phys_addr;
|
||||||
/* when non-zero this device is hosting a 'pfn' instance */
|
/* when non-zero this device is hosting a 'pfn' instance */
|
||||||
phys_addr_t data_offset;
|
phys_addr_t data_offset;
|
||||||
|
unsigned long pfn_flags;
|
||||||
void __pmem *virt_addr;
|
void __pmem *virt_addr;
|
||||||
size_t size;
|
size_t size;
|
||||||
struct badblocks bb;
|
struct badblocks bb;
|
||||||
@ -135,13 +137,13 @@ static int pmem_rw_page(struct block_device *bdev, sector_t sector,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static long pmem_direct_access(struct block_device *bdev, sector_t sector,
|
static long pmem_direct_access(struct block_device *bdev, sector_t sector,
|
||||||
void __pmem **kaddr, unsigned long *pfn)
|
void __pmem **kaddr, pfn_t *pfn)
|
||||||
{
|
{
|
||||||
struct pmem_device *pmem = bdev->bd_disk->private_data;
|
struct pmem_device *pmem = bdev->bd_disk->private_data;
|
||||||
resource_size_t offset = sector * 512 + pmem->data_offset;
|
resource_size_t offset = sector * 512 + pmem->data_offset;
|
||||||
|
|
||||||
*kaddr = pmem->virt_addr + offset;
|
*kaddr = pmem->virt_addr + offset;
|
||||||
*pfn = (pmem->phys_addr + offset) >> PAGE_SHIFT;
|
*pfn = phys_to_pfn_t(pmem->phys_addr + offset, pmem->pfn_flags);
|
||||||
|
|
||||||
return pmem->size - offset;
|
return pmem->size - offset;
|
||||||
}
|
}
|
||||||
@ -174,9 +176,11 @@ static struct pmem_device *pmem_alloc(struct device *dev,
|
|||||||
return ERR_PTR(-EBUSY);
|
return ERR_PTR(-EBUSY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pmem_should_map_pages(dev))
|
pmem->pfn_flags = PFN_DEV;
|
||||||
|
if (pmem_should_map_pages(dev)) {
|
||||||
pmem->virt_addr = (void __pmem *) devm_memremap_pages(dev, res);
|
pmem->virt_addr = (void __pmem *) devm_memremap_pages(dev, res);
|
||||||
else
|
pmem->pfn_flags |= PFN_MAP;
|
||||||
|
} else
|
||||||
pmem->virt_addr = (void __pmem *) devm_memremap(dev,
|
pmem->virt_addr = (void __pmem *) devm_memremap(dev,
|
||||||
pmem->phys_addr, pmem->size,
|
pmem->phys_addr, pmem->size,
|
||||||
ARCH_MEMREMAP_PMEM);
|
ARCH_MEMREMAP_PMEM);
|
||||||
@ -384,6 +388,7 @@ static int nvdimm_namespace_attach_pfn(struct nd_namespace_common *ndns)
|
|||||||
pmem = dev_get_drvdata(dev);
|
pmem = dev_get_drvdata(dev);
|
||||||
devm_memunmap(dev, (void __force *) pmem->virt_addr);
|
devm_memunmap(dev, (void __force *) pmem->virt_addr);
|
||||||
pmem->virt_addr = (void __pmem *) devm_memremap_pages(dev, &nsio->res);
|
pmem->virt_addr = (void __pmem *) devm_memremap_pages(dev, &nsio->res);
|
||||||
|
pmem->pfn_flags |= PFN_MAP;
|
||||||
if (IS_ERR(pmem->virt_addr)) {
|
if (IS_ERR(pmem->virt_addr)) {
|
||||||
rc = PTR_ERR(pmem->virt_addr);
|
rc = PTR_ERR(pmem->virt_addr);
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pfn_t.h>
|
||||||
#include <asm/extmem.h>
|
#include <asm/extmem.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ static void dcssblk_release(struct gendisk *disk, fmode_t mode);
|
|||||||
static blk_qc_t dcssblk_make_request(struct request_queue *q,
|
static blk_qc_t dcssblk_make_request(struct request_queue *q,
|
||||||
struct bio *bio);
|
struct bio *bio);
|
||||||
static long dcssblk_direct_access(struct block_device *bdev, sector_t secnum,
|
static long dcssblk_direct_access(struct block_device *bdev, sector_t secnum,
|
||||||
void __pmem **kaddr, unsigned long *pfn);
|
void __pmem **kaddr, pfn_t *pfn);
|
||||||
|
|
||||||
static char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0";
|
static char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0";
|
||||||
|
|
||||||
@ -883,20 +884,18 @@ dcssblk_make_request(struct request_queue *q, struct bio *bio)
|
|||||||
|
|
||||||
static long
|
static long
|
||||||
dcssblk_direct_access (struct block_device *bdev, sector_t secnum,
|
dcssblk_direct_access (struct block_device *bdev, sector_t secnum,
|
||||||
void __pmem **kaddr, unsigned long *pfn)
|
void __pmem **kaddr, pfn_t *pfn)
|
||||||
{
|
{
|
||||||
struct dcssblk_dev_info *dev_info;
|
struct dcssblk_dev_info *dev_info;
|
||||||
unsigned long offset, dev_sz;
|
unsigned long offset, dev_sz;
|
||||||
void *addr;
|
|
||||||
|
|
||||||
dev_info = bdev->bd_disk->private_data;
|
dev_info = bdev->bd_disk->private_data;
|
||||||
if (!dev_info)
|
if (!dev_info)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
dev_sz = dev_info->end - dev_info->start;
|
dev_sz = dev_info->end - dev_info->start;
|
||||||
offset = secnum * 512;
|
offset = secnum * 512;
|
||||||
addr = (void *) (dev_info->start + offset);
|
*kaddr = (void __pmem *) (dev_info->start + offset);
|
||||||
*pfn = virt_to_phys(addr) >> PAGE_SHIFT;
|
*pfn = __pfn_to_pfn_t(PFN_DOWN(dev_info->start + offset), PFN_DEV);
|
||||||
*kaddr = (void __pmem *) addr;
|
|
||||||
|
|
||||||
return dev_sz - offset;
|
return dev_sz - offset;
|
||||||
}
|
}
|
||||||
|
11
fs/dax.c
11
fs/dax.c
@ -28,6 +28,7 @@
|
|||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/uio.h>
|
#include <linux/uio.h>
|
||||||
#include <linux/vmstat.h>
|
#include <linux/vmstat.h>
|
||||||
|
#include <linux/pfn_t.h>
|
||||||
#include <linux/sizes.h>
|
#include <linux/sizes.h>
|
||||||
|
|
||||||
static long dax_map_atomic(struct block_device *bdev, struct blk_dax_ctl *dax)
|
static long dax_map_atomic(struct block_device *bdev, struct blk_dax_ctl *dax)
|
||||||
@ -362,7 +363,7 @@ static int dax_insert_mapping(struct inode *inode, struct buffer_head *bh,
|
|||||||
}
|
}
|
||||||
dax_unmap_atomic(bdev, &dax);
|
dax_unmap_atomic(bdev, &dax);
|
||||||
|
|
||||||
error = vm_insert_mixed(vma, vaddr, dax.pfn);
|
error = vm_insert_mixed(vma, vaddr, pfn_t_to_pfn(dax.pfn));
|
||||||
|
|
||||||
out:
|
out:
|
||||||
i_mmap_unlock_read(mapping);
|
i_mmap_unlock_read(mapping);
|
||||||
@ -667,7 +668,8 @@ int __dax_pmd_fault(struct vm_area_struct *vma, unsigned long address,
|
|||||||
result = VM_FAULT_SIGBUS;
|
result = VM_FAULT_SIGBUS;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if ((length < PMD_SIZE) || (dax.pfn & PG_PMD_COLOUR)) {
|
if (length < PMD_SIZE
|
||||||
|
|| (pfn_t_to_pfn(dax.pfn) & PG_PMD_COLOUR)) {
|
||||||
dax_unmap_atomic(bdev, &dax);
|
dax_unmap_atomic(bdev, &dax);
|
||||||
goto fallback;
|
goto fallback;
|
||||||
}
|
}
|
||||||
@ -676,7 +678,7 @@ int __dax_pmd_fault(struct vm_area_struct *vma, unsigned long address,
|
|||||||
* TODO: teach vmf_insert_pfn_pmd() to support
|
* TODO: teach vmf_insert_pfn_pmd() to support
|
||||||
* 'pte_special' for pmds
|
* 'pte_special' for pmds
|
||||||
*/
|
*/
|
||||||
if (pfn_valid(dax.pfn)) {
|
if (pfn_t_has_page(dax.pfn)) {
|
||||||
dax_unmap_atomic(bdev, &dax);
|
dax_unmap_atomic(bdev, &dax);
|
||||||
goto fallback;
|
goto fallback;
|
||||||
}
|
}
|
||||||
@ -690,7 +692,8 @@ int __dax_pmd_fault(struct vm_area_struct *vma, unsigned long address,
|
|||||||
}
|
}
|
||||||
dax_unmap_atomic(bdev, &dax);
|
dax_unmap_atomic(bdev, &dax);
|
||||||
|
|
||||||
result |= vmf_insert_pfn_pmd(vma, address, pmd, dax.pfn, write);
|
result |= vmf_insert_pfn_pmd(vma, address, pmd,
|
||||||
|
pfn_t_to_pfn(dax.pfn), write);
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <linux/backing-dev-defs.h>
|
#include <linux/backing-dev-defs.h>
|
||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <linux/mempool.h>
|
#include <linux/mempool.h>
|
||||||
|
#include <linux/pfn.h>
|
||||||
#include <linux/bio.h>
|
#include <linux/bio.h>
|
||||||
#include <linux/stringify.h>
|
#include <linux/stringify.h>
|
||||||
#include <linux/gfp.h>
|
#include <linux/gfp.h>
|
||||||
@ -1628,7 +1629,7 @@ struct blk_dax_ctl {
|
|||||||
sector_t sector;
|
sector_t sector;
|
||||||
void __pmem *addr;
|
void __pmem *addr;
|
||||||
long size;
|
long size;
|
||||||
unsigned long pfn;
|
pfn_t pfn;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct block_device_operations {
|
struct block_device_operations {
|
||||||
@ -1638,7 +1639,7 @@ struct block_device_operations {
|
|||||||
int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
|
int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
|
||||||
int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
|
int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
|
||||||
long (*direct_access)(struct block_device *, sector_t, void __pmem **,
|
long (*direct_access)(struct block_device *, sector_t, void __pmem **,
|
||||||
unsigned long *pfn);
|
pfn_t *);
|
||||||
unsigned int (*check_events) (struct gendisk *disk,
|
unsigned int (*check_events) (struct gendisk *disk,
|
||||||
unsigned int clearing);
|
unsigned int clearing);
|
||||||
/* ->media_changed() is DEPRECATED, use ->check_events() instead */
|
/* ->media_changed() is DEPRECATED, use ->check_events() instead */
|
||||||
|
@ -3,6 +3,15 @@
|
|||||||
|
|
||||||
#ifndef __ASSEMBLY__
|
#ifndef __ASSEMBLY__
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pfn_t: encapsulates a page-frame number that is optionally backed
|
||||||
|
* by memmap (struct page). Whether a pfn_t has a 'struct page'
|
||||||
|
* backing is indicated by flags in the high bits of the value.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
unsigned long val;
|
||||||
|
} pfn_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define PFN_ALIGN(x) (((unsigned long)(x) + (PAGE_SIZE - 1)) & PAGE_MASK)
|
#define PFN_ALIGN(x) (((unsigned long)(x) + (PAGE_SIZE - 1)) & PAGE_MASK)
|
||||||
|
67
include/linux/pfn_t.h
Normal file
67
include/linux/pfn_t.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#ifndef _LINUX_PFN_T_H_
|
||||||
|
#define _LINUX_PFN_T_H_
|
||||||
|
#include <linux/mm.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PFN_FLAGS_MASK - mask of all the possible valid pfn_t flags
|
||||||
|
* PFN_SG_CHAIN - pfn is a pointer to the next scatterlist entry
|
||||||
|
* PFN_SG_LAST - pfn references a page and is the last scatterlist entry
|
||||||
|
* PFN_DEV - pfn is not covered by system memmap by default
|
||||||
|
* PFN_MAP - pfn has a dynamic page mapping established by a device driver
|
||||||
|
*/
|
||||||
|
#define PFN_FLAGS_MASK (((unsigned long) ~PAGE_MASK) \
|
||||||
|
<< (BITS_PER_LONG - PAGE_SHIFT))
|
||||||
|
#define PFN_SG_CHAIN (1UL << (BITS_PER_LONG - 1))
|
||||||
|
#define PFN_SG_LAST (1UL << (BITS_PER_LONG - 2))
|
||||||
|
#define PFN_DEV (1UL << (BITS_PER_LONG - 3))
|
||||||
|
#define PFN_MAP (1UL << (BITS_PER_LONG - 4))
|
||||||
|
|
||||||
|
static inline pfn_t __pfn_to_pfn_t(unsigned long pfn, unsigned long flags)
|
||||||
|
{
|
||||||
|
pfn_t pfn_t = { .val = pfn | (flags & PFN_FLAGS_MASK), };
|
||||||
|
|
||||||
|
return pfn_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* a default pfn to pfn_t conversion assumes that @pfn is pfn_valid() */
|
||||||
|
static inline pfn_t pfn_to_pfn_t(unsigned long pfn)
|
||||||
|
{
|
||||||
|
return __pfn_to_pfn_t(pfn, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern pfn_t phys_to_pfn_t(dma_addr_t addr, unsigned long flags);
|
||||||
|
|
||||||
|
static inline bool pfn_t_has_page(pfn_t pfn)
|
||||||
|
{
|
||||||
|
return (pfn.val & PFN_MAP) == PFN_MAP || (pfn.val & PFN_DEV) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long pfn_t_to_pfn(pfn_t pfn)
|
||||||
|
{
|
||||||
|
return pfn.val & ~PFN_FLAGS_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct page *pfn_t_to_page(pfn_t pfn)
|
||||||
|
{
|
||||||
|
if (pfn_t_has_page(pfn))
|
||||||
|
return pfn_to_page(pfn_t_to_pfn(pfn));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline dma_addr_t pfn_t_to_phys(pfn_t pfn)
|
||||||
|
{
|
||||||
|
return PFN_PHYS(pfn_t_to_pfn(pfn));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *pfn_t_to_virt(pfn_t pfn)
|
||||||
|
{
|
||||||
|
if (pfn_t_has_page(pfn))
|
||||||
|
return __va(pfn_t_to_phys(pfn));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline pfn_t page_to_pfn_t(struct page *page)
|
||||||
|
{
|
||||||
|
return pfn_to_pfn_t(page_to_pfn(page));
|
||||||
|
}
|
||||||
|
#endif /* _LINUX_PFN_T_H_ */
|
@ -12,6 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
#include <linux/pfn_t.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/memory_hotplug.h>
|
#include <linux/memory_hotplug.h>
|
||||||
@ -147,6 +148,12 @@ void devm_memunmap(struct device *dev, void *addr)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(devm_memunmap);
|
EXPORT_SYMBOL(devm_memunmap);
|
||||||
|
|
||||||
|
pfn_t phys_to_pfn_t(dma_addr_t addr, unsigned long flags)
|
||||||
|
{
|
||||||
|
return __pfn_to_pfn_t(addr >> PAGE_SHIFT, flags);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(phys_to_pfn_t);
|
||||||
|
|
||||||
#ifdef CONFIG_ZONE_DEVICE
|
#ifdef CONFIG_ZONE_DEVICE
|
||||||
struct page_map {
|
struct page_map {
|
||||||
struct resource res;
|
struct resource res;
|
||||||
|
Loading…
Reference in New Issue
Block a user