mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 11:56:58 +00:00
Pull MM updates from Andrew Morton:
"__vmalloc()/kvmalloc() and no-block support" (Uladzislau Rezki)
Rework the vmalloc() code to support non-blocking allocations
(GFP_ATOIC, GFP_NOWAIT)
"ksm: fix exec/fork inheritance" (xu xin)
Fix a rare case where the KSM MMF_VM_MERGE_ANY prctl state is not
inherited across fork/exec
"mm/zswap: misc cleanup of code and documentations" (SeongJae Park)
Some light maintenance work on the zswap code
"mm/page_owner: add debugfs files 'show_handles' and 'show_stacks_handles'" (Mauricio Faria de Oliveira)
Enhance the /sys/kernel/debug/page_owner debug feature by adding
unique identifiers to differentiate the various stack traces so
that userspace monitoring tools can better match stack traces over
time
"mm/page_alloc: pcp->batch cleanups" (Joshua Hahn)
Minor alterations to the page allocator's per-cpu-pages feature
"Improve UFFDIO_MOVE scalability by removing anon_vma lock" (Lokesh Gidra)
Address a scalability issue in userfaultfd's UFFDIO_MOVE operation
"kasan: cleanups for kasan_enabled() checks" (Sabyrzhan Tasbolatov)
"drivers/base/node: fold node register and unregister functions" (Donet Tom)
Clean up the NUMA node handling code a little
"mm: some optimizations for prot numa" (Kefeng Wang)
Cleanups and small optimizations to the NUMA allocation hinting
code
"mm/page_alloc: Batch callers of free_pcppages_bulk" (Joshua Hahn)
Address long lock hold times at boot on large machines. These were
causing (harmless) softlockup warnings
"optimize the logic for handling dirty file folios during reclaim" (Baolin Wang)
Remove some now-unnecessary work from page reclaim
"mm/damon: allow DAMOS auto-tuned for per-memcg per-node memory usage" (SeongJae Park)
Enhance the DAMOS auto-tuning feature
"mm/damon: fixes for address alignment issues in DAMON_LRU_SORT and DAMON_RECLAIM" (Quanmin Yan)
Fix DAMON_LRU_SORT and DAMON_RECLAIM with certain userspace
configuration
"expand mmap_prepare functionality, port more users" (Lorenzo Stoakes)
Enhance the new(ish) file_operations.mmap_prepare() method and port
additional callsites from the old ->mmap() over to ->mmap_prepare()
"Fix stale IOTLB entries for kernel address space" (Lu Baolu)
Fix a bug (and possible security issue on non-x86) in the IOMMU
code. In some situations the IOMMU could be left hanging onto a
stale kernel pagetable entry
"mm/huge_memory: cleanup __split_unmapped_folio()" (Wei Yang)
Clean up and optimize the folio splitting code
"mm, swap: misc cleanup and bugfix" (Kairui Song)
Some cleanups and a minor fix in the swap discard code
"mm/damon: misc documentation fixups" (SeongJae Park)
"mm/damon: support pin-point targets removal" (SeongJae Park)
Permit userspace to remove a specific monitoring target in the
middle of the current targets list
"mm: MISC follow-up patches for linux/pgalloc.h" (Harry Yoo)
A couple of cleanups related to mm header file inclusion
"mm/swapfile.c: select swap devices of default priority round robin" (Baoquan He)
improve the selection of swap devices for NUMA machines
"mm: Convert memory block states (MEM_*) macros to enums" (Israel Batista)
Change the memory block labels from macros to enums so they will
appear in kernel debug info
"ksm: perform a range-walk to jump over holes in break_ksm" (Pedro Demarchi Gomes)
Address an inefficiency when KSM unmerges an address range
"mm/damon/tests: fix memory bugs in kunit tests" (SeongJae Park)
Fix leaks and unhandled malloc() failures in DAMON userspace unit
tests
"some cleanups for pageout()" (Baolin Wang)
Clean up a couple of minor things in the page scanner's
writeback-for-eviction code
"mm/hugetlb: refactor sysfs/sysctl interfaces" (Hui Zhu)
Move hugetlb's sysfs/sysctl handling code into a new file
"introduce VM_MAYBE_GUARD and make it sticky" (Lorenzo Stoakes)
Make the VMA guard regions available in /proc/pid/smaps and
improves the mergeability of guarded VMAs
"mm: perform guard region install/remove under VMA lock" (Lorenzo Stoakes)
Reduce mmap lock contention for callers performing VMA guard region
operations
"vma_start_write_killable" (Matthew Wilcox)
Start work on permitting applications to be killed when they are
waiting on a read_lock on the VMA lock
"mm/damon/tests: add more tests for online parameters commit" (SeongJae Park)
Add additional userspace testing of DAMON's "commit" feature
"mm/damon: misc cleanups" (SeongJae Park)
"make VM_SOFTDIRTY a sticky VMA flag" (Lorenzo Stoakes)
Address the possible loss of a VMA's VM_SOFTDIRTY flag when that
VMA is merged with another
"mm: support device-private THP" (Balbir Singh)
Introduce support for Transparent Huge Page (THP) migration in zone
device-private memory
"Optimize folio split in memory failure" (Zi Yan)
"mm/huge_memory: Define split_type and consolidate split support checks" (Wei Yang)
Some more cleanups in the folio splitting code
"mm: remove is_swap_[pte, pmd]() + non-swap entries, introduce leaf entries" (Lorenzo Stoakes)
Clean up our handling of pagetable leaf entries by introducing the
concept of 'software leaf entries', of type softleaf_t
"reparent the THP split queue" (Muchun Song)
Reparent the THP split queue to its parent memcg. This is in
preparation for addressing the long-standing "dying memcg" problem,
wherein dead memcg's linger for too long, consuming memory
resources
"unify PMD scan results and remove redundant cleanup" (Wei Yang)
A little cleanup in the hugepage collapse code
"zram: introduce writeback bio batching" (Sergey Senozhatsky)
Improve zram writeback efficiency by introducing batched bio
writeback support
"memcg: cleanup the memcg stats interfaces" (Shakeel Butt)
Clean up our handling of the interrupt safety of some memcg stats
"make vmalloc gfp flags usage more apparent" (Vishal Moola)
Clean up vmalloc's handling of incoming GFP flags
"mm: Add soft-dirty and uffd-wp support for RISC-V" (Chunyan Zhang)
Teach soft dirty and userfaultfd write protect tracking to use
RISC-V's Svrsw60t59b extension
"mm: swap: small fixes and comment cleanups" (Youngjun Park)
Fix a small bug and clean up some of the swap code
"initial work on making VMA flags a bitmap" (Lorenzo Stoakes)
Start work on converting the vma struct's flags to a bitmap, so we
stop running out of them, especially on 32-bit
"mm/swapfile: fix and cleanup swap list iterations" (Youngjun Park)
Address a possible bug in the swap discard code and clean things
up a little
[ This merge also reverts commit ebb9aeb980 ("vfio/nvgrace-gpu:
register device memory for poison handling") because it looks
broken to me, I've asked for clarification - Linus ]
* tag 'mm-stable-2025-12-03-21-26' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: (321 commits)
mm: fix vma_start_write_killable() signal handling
mm/swapfile: use plist_for_each_entry in __folio_throttle_swaprate
mm/swapfile: fix list iteration when next node is removed during discard
fs/proc/task_mmu.c: fix make_uffd_wp_huge_pte() huge pte handling
mm/kfence: add reboot notifier to disable KFENCE on shutdown
memcg: remove inc/dec_lruvec_kmem_state helpers
selftests/mm/uffd: initialize char variable to Null
mm: fix DEBUG_RODATA_TEST indentation in Kconfig
mm: introduce VMA flags bitmap type
tools/testing/vma: eliminate dependency on vma->__vm_flags
mm: simplify and rename mm flags function for clarity
mm: declare VMA flags by bit
zram: fix a spelling mistake
mm/page_alloc: optimize lowmem_reserve max lookup using its semantic monotonicity
mm/vmscan: skip increasing kswapd_failures when reclaim was boosted
pagemap: update BUDDY flag documentation
mm: swap: remove scan_swap_map_slots() references from comments
mm: swap: change swap_alloc_slow() to void
mm, swap: remove redundant comment for read_swap_cache_async
mm, swap: use SWP_SOLIDSTATE to determine if swap is rotational
...
591 lines
14 KiB
C
591 lines
14 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* SCLP Store Data support and sysfs interface
|
|
*
|
|
* Copyright IBM Corp. 2017
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "sclp_sd: " fmt
|
|
|
|
#include <linux/completion.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/kobject.h>
|
|
#include <linux/list.h>
|
|
#include <linux/printk.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/async.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/pgalloc.h>
|
|
|
|
#include "sclp.h"
|
|
|
|
#define SD_EQ_STORE_DATA 0
|
|
#define SD_EQ_HALT 1
|
|
#define SD_EQ_SIZE 2
|
|
|
|
#define SD_DI_CONFIG 3
|
|
|
|
#define SD_TIMEOUT msecs_to_jiffies(30000)
|
|
|
|
struct sclp_sd_evbuf {
|
|
struct evbuf_header hdr;
|
|
u8 eq;
|
|
u8 di;
|
|
u8 rflags;
|
|
u64 :56;
|
|
u32 id;
|
|
u16 :16;
|
|
u8 fmt;
|
|
u8 status;
|
|
u64 sat;
|
|
u64 sa;
|
|
u32 esize;
|
|
u32 dsize;
|
|
} __packed;
|
|
|
|
struct sclp_sd_sccb {
|
|
struct sccb_header hdr;
|
|
struct sclp_sd_evbuf evbuf;
|
|
} __packed __aligned(PAGE_SIZE);
|
|
|
|
/**
|
|
* struct sclp_sd_data - Result of a Store Data request
|
|
* @esize_bytes: Resulting esize in bytes
|
|
* @dsize_bytes: Resulting dsize in bytes
|
|
* @data: Pointer to data - must be released using vfree()
|
|
*/
|
|
struct sclp_sd_data {
|
|
size_t esize_bytes;
|
|
size_t dsize_bytes;
|
|
void *data;
|
|
};
|
|
|
|
/**
|
|
* struct sclp_sd_listener - Listener for asynchronous Store Data response
|
|
* @list: For enqueueing this struct
|
|
* @id: Event ID of response to listen for
|
|
* @completion: Can be used to wait for response
|
|
* @evbuf: Contains the resulting Store Data response after completion
|
|
*/
|
|
struct sclp_sd_listener {
|
|
struct list_head list;
|
|
u32 id;
|
|
struct completion completion;
|
|
struct sclp_sd_evbuf evbuf;
|
|
};
|
|
|
|
/**
|
|
* struct sclp_sd_file - Sysfs representation of a Store Data entity
|
|
* @kobj: Kobject
|
|
* @data_attr: Attribute for accessing data contents
|
|
* @data_mutex: Mutex to serialize access and updates to @data
|
|
* @data: Data associated with this entity
|
|
* @di: DI value associated with this entity
|
|
*/
|
|
struct sclp_sd_file {
|
|
struct kobject kobj;
|
|
struct bin_attribute data_attr;
|
|
struct mutex data_mutex;
|
|
struct sclp_sd_data data;
|
|
u8 di;
|
|
};
|
|
#define to_sd_file(x) container_of(x, struct sclp_sd_file, kobj)
|
|
|
|
static struct kset *sclp_sd_kset;
|
|
static struct sclp_sd_file *config_file;
|
|
|
|
static LIST_HEAD(sclp_sd_queue);
|
|
static DEFINE_SPINLOCK(sclp_sd_queue_lock);
|
|
|
|
/**
|
|
* sclp_sd_listener_add() - Add listener for Store Data responses
|
|
* @listener: Listener to add
|
|
*/
|
|
static void sclp_sd_listener_add(struct sclp_sd_listener *listener)
|
|
{
|
|
spin_lock_irq(&sclp_sd_queue_lock);
|
|
list_add_tail(&listener->list, &sclp_sd_queue);
|
|
spin_unlock_irq(&sclp_sd_queue_lock);
|
|
}
|
|
|
|
/**
|
|
* sclp_sd_listener_remove() - Remove listener for Store Data responses
|
|
* @listener: Listener to remove
|
|
*/
|
|
static void sclp_sd_listener_remove(struct sclp_sd_listener *listener)
|
|
{
|
|
spin_lock_irq(&sclp_sd_queue_lock);
|
|
list_del(&listener->list);
|
|
spin_unlock_irq(&sclp_sd_queue_lock);
|
|
}
|
|
|
|
/**
|
|
* sclp_sd_listener_init() - Initialize a Store Data response listener
|
|
* @listener: Response listener to initialize
|
|
* @id: Event ID to listen for
|
|
*
|
|
* Initialize a listener for asynchronous Store Data responses. This listener
|
|
* can afterwards be used to wait for a specific response and to retrieve
|
|
* the associated response data.
|
|
*/
|
|
static void sclp_sd_listener_init(struct sclp_sd_listener *listener, u32 id)
|
|
{
|
|
memset(listener, 0, sizeof(*listener));
|
|
listener->id = id;
|
|
init_completion(&listener->completion);
|
|
}
|
|
|
|
/**
|
|
* sclp_sd_receiver() - Receiver for Store Data events
|
|
* @evbuf_hdr: Header of received events
|
|
*
|
|
* Process Store Data events and complete listeners with matching event IDs.
|
|
*/
|
|
static void sclp_sd_receiver(struct evbuf_header *evbuf_hdr)
|
|
{
|
|
struct sclp_sd_evbuf *evbuf = (struct sclp_sd_evbuf *) evbuf_hdr;
|
|
struct sclp_sd_listener *listener;
|
|
int found = 0;
|
|
|
|
pr_debug("received event (id=0x%08x)\n", evbuf->id);
|
|
spin_lock(&sclp_sd_queue_lock);
|
|
list_for_each_entry(listener, &sclp_sd_queue, list) {
|
|
if (listener->id != evbuf->id)
|
|
continue;
|
|
|
|
listener->evbuf = *evbuf;
|
|
complete(&listener->completion);
|
|
found = 1;
|
|
break;
|
|
}
|
|
spin_unlock(&sclp_sd_queue_lock);
|
|
|
|
if (!found)
|
|
pr_debug("unsolicited event (id=0x%08x)\n", evbuf->id);
|
|
}
|
|
|
|
static struct sclp_register sclp_sd_register = {
|
|
.send_mask = EVTYP_STORE_DATA_MASK,
|
|
.receive_mask = EVTYP_STORE_DATA_MASK,
|
|
.receiver_fn = sclp_sd_receiver,
|
|
};
|
|
|
|
/**
|
|
* sclp_sd_sync() - Perform Store Data request synchronously
|
|
* @page: Address of work page - must be below 2GB
|
|
* @eq: Input EQ value
|
|
* @di: Input DI value
|
|
* @sat: Input SAT value
|
|
* @sa: Input SA value used to specify the address of the target buffer
|
|
* @dsize_ptr: Optional pointer to input and output DSIZE value
|
|
* @esize_ptr: Optional pointer to output ESIZE value
|
|
*
|
|
* Perform Store Data request with specified parameters and wait for completion.
|
|
*
|
|
* Return %0 on success and store resulting DSIZE and ESIZE values in
|
|
* @dsize_ptr and @esize_ptr (if provided). Return non-zero on error.
|
|
*/
|
|
static int sclp_sd_sync(unsigned long page, u8 eq, u8 di, u64 sat, u64 sa,
|
|
u32 *dsize_ptr, u32 *esize_ptr)
|
|
{
|
|
struct sclp_sd_sccb *sccb = (void *) page;
|
|
struct sclp_sd_listener listener;
|
|
struct sclp_sd_evbuf *evbuf;
|
|
int rc;
|
|
|
|
if (!sclp_sd_register.sclp_send_mask ||
|
|
!sclp_sd_register.sclp_receive_mask)
|
|
return -EIO;
|
|
|
|
sclp_sd_listener_init(&listener, __pa(sccb));
|
|
sclp_sd_listener_add(&listener);
|
|
|
|
/* Prepare SCCB */
|
|
memset(sccb, 0, PAGE_SIZE);
|
|
sccb->hdr.length = sizeof(sccb->hdr) + sizeof(sccb->evbuf);
|
|
evbuf = &sccb->evbuf;
|
|
evbuf->hdr.length = sizeof(*evbuf);
|
|
evbuf->hdr.type = EVTYP_STORE_DATA;
|
|
evbuf->eq = eq;
|
|
evbuf->di = di;
|
|
evbuf->id = listener.id;
|
|
evbuf->fmt = 1;
|
|
evbuf->sat = sat;
|
|
evbuf->sa = sa;
|
|
if (dsize_ptr)
|
|
evbuf->dsize = *dsize_ptr;
|
|
|
|
/* Perform command */
|
|
pr_debug("request (eq=%d, di=%d, id=0x%08x)\n", eq, di, listener.id);
|
|
rc = sclp_sync_request(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
|
|
pr_debug("request done (rc=%d)\n", rc);
|
|
if (rc)
|
|
goto out;
|
|
|
|
/* Evaluate response */
|
|
if (sccb->hdr.response_code == 0x73f0) {
|
|
pr_debug("event not supported\n");
|
|
rc = -EIO;
|
|
goto out_remove;
|
|
}
|
|
if (sccb->hdr.response_code != 0x0020 || !(evbuf->hdr.flags & 0x80)) {
|
|
rc = -EIO;
|
|
goto out;
|
|
}
|
|
if (!(evbuf->rflags & 0x80)) {
|
|
rc = wait_for_completion_interruptible_timeout(&listener.completion, SD_TIMEOUT);
|
|
if (rc == 0)
|
|
rc = -ETIME;
|
|
if (rc < 0)
|
|
goto out;
|
|
rc = 0;
|
|
evbuf = &listener.evbuf;
|
|
}
|
|
switch (evbuf->status) {
|
|
case 0:
|
|
if (dsize_ptr)
|
|
*dsize_ptr = evbuf->dsize;
|
|
if (esize_ptr)
|
|
*esize_ptr = evbuf->esize;
|
|
pr_debug("success (dsize=%u, esize=%u)\n", evbuf->dsize,
|
|
evbuf->esize);
|
|
break;
|
|
case 3:
|
|
rc = -ENOENT;
|
|
break;
|
|
default:
|
|
rc = -EIO;
|
|
break;
|
|
|
|
}
|
|
|
|
out:
|
|
if (rc && rc != -ENOENT) {
|
|
/* Provide some information about what went wrong */
|
|
pr_warn("Store Data request failed (eq=%d, di=%d, "
|
|
"response=0x%04x, flags=0x%02x, status=%d, rc=%d)\n",
|
|
eq, di, sccb->hdr.response_code, evbuf->hdr.flags,
|
|
evbuf->status, rc);
|
|
}
|
|
|
|
out_remove:
|
|
sclp_sd_listener_remove(&listener);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* sclp_sd_store_data() - Obtain data for specified Store Data entity
|
|
* @result: Resulting data
|
|
* @di: DI value associated with this entity
|
|
*
|
|
* Perform a series of Store Data requests to obtain the size and contents of
|
|
* the specified Store Data entity.
|
|
*
|
|
* Return:
|
|
* %0: Success - result is stored in @result. @result->data must be
|
|
* released using vfree() after use.
|
|
* %-ENOENT: No data available for this entity
|
|
* %<0: Other error
|
|
*/
|
|
static int sclp_sd_store_data(struct sclp_sd_data *result, u8 di)
|
|
{
|
|
u32 dsize = 0, esize = 0;
|
|
unsigned long page, asce = 0;
|
|
void *data = NULL;
|
|
int rc;
|
|
|
|
page = __get_free_page(GFP_KERNEL | GFP_DMA);
|
|
if (!page)
|
|
return -ENOMEM;
|
|
|
|
/* Get size */
|
|
rc = sclp_sd_sync(page, SD_EQ_SIZE, di, 0, 0, &dsize, &esize);
|
|
if (rc)
|
|
goto out;
|
|
if (dsize == 0)
|
|
goto out_result;
|
|
|
|
/* Allocate memory */
|
|
data = vzalloc(array_size((size_t)dsize, PAGE_SIZE));
|
|
if (!data) {
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
/* Get translation table for buffer */
|
|
asce = base_asce_alloc((unsigned long) data, dsize);
|
|
if (!asce) {
|
|
vfree(data);
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
/* Get data */
|
|
rc = sclp_sd_sync(page, SD_EQ_STORE_DATA, di, asce, (u64) data, &dsize,
|
|
&esize);
|
|
if (rc) {
|
|
/* Cancel running request if interrupted or timed out */
|
|
if (rc == -ERESTARTSYS || rc == -ETIME) {
|
|
if (sclp_sd_sync(page, SD_EQ_HALT, di, 0, 0, NULL, NULL)) {
|
|
pr_warn("Could not stop Store Data request - leaking at least %zu bytes\n",
|
|
(size_t)dsize * PAGE_SIZE);
|
|
data = NULL;
|
|
asce = 0;
|
|
}
|
|
}
|
|
vfree(data);
|
|
goto out;
|
|
}
|
|
|
|
out_result:
|
|
result->esize_bytes = (size_t) esize * PAGE_SIZE;
|
|
result->dsize_bytes = (size_t) dsize * PAGE_SIZE;
|
|
result->data = data;
|
|
|
|
out:
|
|
base_asce_free(asce);
|
|
free_page(page);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* sclp_sd_data_reset() - Reset Store Data result buffer
|
|
* @data: Data buffer to reset
|
|
*
|
|
* Reset @data to initial state and release associated memory.
|
|
*/
|
|
static void sclp_sd_data_reset(struct sclp_sd_data *data)
|
|
{
|
|
vfree(data->data);
|
|
data->data = NULL;
|
|
data->dsize_bytes = 0;
|
|
data->esize_bytes = 0;
|
|
}
|
|
|
|
/**
|
|
* sclp_sd_file_release() - Release function for sclp_sd_file object
|
|
* @kobj: Kobject embedded in sclp_sd_file object
|
|
*/
|
|
static void sclp_sd_file_release(struct kobject *kobj)
|
|
{
|
|
struct sclp_sd_file *sd_file = to_sd_file(kobj);
|
|
|
|
sclp_sd_data_reset(&sd_file->data);
|
|
kfree(sd_file);
|
|
}
|
|
|
|
/**
|
|
* sclp_sd_file_update() - Update contents of sclp_sd_file object
|
|
* @sd_file: Object to update
|
|
*
|
|
* Obtain the current version of data associated with the Store Data entity
|
|
* @sd_file.
|
|
*
|
|
* On success, return %0 and generate a KOBJ_CHANGE event to indicate that the
|
|
* data may have changed. Return non-zero otherwise.
|
|
*/
|
|
static int sclp_sd_file_update(struct sclp_sd_file *sd_file)
|
|
{
|
|
const char *name = kobject_name(&sd_file->kobj);
|
|
struct sclp_sd_data data;
|
|
int rc;
|
|
|
|
rc = sclp_sd_store_data(&data, sd_file->di);
|
|
if (rc) {
|
|
if (rc == -ENOENT) {
|
|
pr_info("No data is available for the %s data entity\n",
|
|
name);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
mutex_lock(&sd_file->data_mutex);
|
|
sclp_sd_data_reset(&sd_file->data);
|
|
sd_file->data = data;
|
|
mutex_unlock(&sd_file->data_mutex);
|
|
|
|
pr_info("A %zu-byte %s data entity was retrieved\n", data.dsize_bytes,
|
|
name);
|
|
kobject_uevent(&sd_file->kobj, KOBJ_CHANGE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* sclp_sd_file_update_async() - Wrapper for asynchronous update call
|
|
* @data: Object to update
|
|
* @cookie: Unused
|
|
*/
|
|
static void sclp_sd_file_update_async(void *data, async_cookie_t cookie)
|
|
{
|
|
struct sclp_sd_file *sd_file = data;
|
|
|
|
sclp_sd_file_update(sd_file);
|
|
}
|
|
|
|
/**
|
|
* reload_store() - Store function for "reload" sysfs attribute
|
|
* @kobj: Kobject of sclp_sd_file object
|
|
* @attr: Reload attribute
|
|
* @buf: Data written to sysfs attribute
|
|
* @count: Count of bytes written
|
|
*
|
|
* Initiate a reload of the data associated with an sclp_sd_file object.
|
|
*/
|
|
static ssize_t reload_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct sclp_sd_file *sd_file = to_sd_file(kobj);
|
|
|
|
sclp_sd_file_update(sd_file);
|
|
|
|
return count;
|
|
}
|
|
|
|
static struct kobj_attribute reload_attr = __ATTR_WO(reload);
|
|
|
|
static struct attribute *sclp_sd_file_default_attrs[] = {
|
|
&reload_attr.attr,
|
|
NULL,
|
|
};
|
|
ATTRIBUTE_GROUPS(sclp_sd_file_default);
|
|
|
|
static struct kobj_type sclp_sd_file_ktype = {
|
|
.sysfs_ops = &kobj_sysfs_ops,
|
|
.release = sclp_sd_file_release,
|
|
.default_groups = sclp_sd_file_default_groups,
|
|
};
|
|
|
|
/**
|
|
* data_read() - Read function for "data" sysfs attribute
|
|
* @file: Open file pointer
|
|
* @kobj: Kobject of sclp_sd_file object
|
|
* @attr: Data attribute
|
|
* @buffer: Target buffer
|
|
* @off: Requested file offset
|
|
* @size: Requested number of bytes
|
|
*
|
|
* Store the requested portion of the Store Data entity contents into the
|
|
* specified buffer. Return the number of bytes stored on success, or %0
|
|
* on EOF.
|
|
*/
|
|
static ssize_t data_read(struct file *file, struct kobject *kobj,
|
|
const struct bin_attribute *attr, char *buffer,
|
|
loff_t off, size_t size)
|
|
{
|
|
struct sclp_sd_file *sd_file = to_sd_file(kobj);
|
|
size_t data_size;
|
|
char *data;
|
|
|
|
mutex_lock(&sd_file->data_mutex);
|
|
|
|
data = sd_file->data.data;
|
|
data_size = sd_file->data.dsize_bytes;
|
|
if (!data || off >= data_size) {
|
|
size = 0;
|
|
} else {
|
|
if (off + size > data_size)
|
|
size = data_size - off;
|
|
memcpy(buffer, data + off, size);
|
|
}
|
|
|
|
mutex_unlock(&sd_file->data_mutex);
|
|
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
* sclp_sd_file_create() - Add a sysfs file representing a Store Data entity
|
|
* @name: Name of file
|
|
* @di: DI value associated with this entity
|
|
*
|
|
* Create a sysfs directory with the given @name located under
|
|
*
|
|
* /sys/firmware/sclp_sd/
|
|
*
|
|
* The files in this directory can be used to access the contents of the Store
|
|
* Data entity associated with @DI.
|
|
*
|
|
* Return pointer to resulting sclp_sd_file object on success, %NULL otherwise.
|
|
* The object must be freed by calling kobject_put() on the embedded kobject
|
|
* pointer after use.
|
|
*/
|
|
static __init struct sclp_sd_file *sclp_sd_file_create(const char *name, u8 di)
|
|
{
|
|
struct sclp_sd_file *sd_file;
|
|
int rc;
|
|
|
|
sd_file = kzalloc(sizeof(*sd_file), GFP_KERNEL);
|
|
if (!sd_file)
|
|
return NULL;
|
|
sd_file->di = di;
|
|
mutex_init(&sd_file->data_mutex);
|
|
|
|
/* Create kobject located under /sys/firmware/sclp_sd/ */
|
|
sd_file->kobj.kset = sclp_sd_kset;
|
|
rc = kobject_init_and_add(&sd_file->kobj, &sclp_sd_file_ktype, NULL,
|
|
"%s", name);
|
|
if (rc) {
|
|
kobject_put(&sd_file->kobj);
|
|
return NULL;
|
|
}
|
|
|
|
sysfs_bin_attr_init(&sd_file->data_attr);
|
|
sd_file->data_attr.attr.name = "data";
|
|
sd_file->data_attr.attr.mode = 0444;
|
|
sd_file->data_attr.read = data_read;
|
|
|
|
rc = sysfs_create_bin_file(&sd_file->kobj, &sd_file->data_attr);
|
|
if (rc) {
|
|
kobject_put(&sd_file->kobj);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* For completeness only - users interested in entity data should listen
|
|
* for KOBJ_CHANGE instead.
|
|
*/
|
|
kobject_uevent(&sd_file->kobj, KOBJ_ADD);
|
|
|
|
/* Don't let a slow Store Data request delay further initialization */
|
|
async_schedule(sclp_sd_file_update_async, sd_file);
|
|
|
|
return sd_file;
|
|
}
|
|
|
|
/**
|
|
* sclp_sd_init() - Initialize sclp_sd support and register sysfs files
|
|
*/
|
|
static __init int sclp_sd_init(void)
|
|
{
|
|
int rc;
|
|
|
|
rc = sclp_register(&sclp_sd_register);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Create kset named "sclp_sd" located under /sys/firmware/ */
|
|
rc = -ENOMEM;
|
|
sclp_sd_kset = kset_create_and_add("sclp_sd", NULL, firmware_kobj);
|
|
if (!sclp_sd_kset)
|
|
goto err_kset;
|
|
|
|
rc = -EINVAL;
|
|
config_file = sclp_sd_file_create("config", SD_DI_CONFIG);
|
|
if (!config_file)
|
|
goto err_config;
|
|
|
|
return 0;
|
|
|
|
err_config:
|
|
kset_unregister(sclp_sd_kset);
|
|
err_kset:
|
|
sclp_unregister(&sclp_sd_register);
|
|
|
|
return rc;
|
|
}
|
|
device_initcall(sclp_sd_init);
|