162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SCLP Store Data support and sysfs interface 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 2017 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define KMSG_COMPONENT "sclp_sd" 962306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/completion.h> 1262306a36Sopenharmony_ci#include <linux/kobject.h> 1362306a36Sopenharmony_ci#include <linux/list.h> 1462306a36Sopenharmony_ci#include <linux/printk.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/vmalloc.h> 1762306a36Sopenharmony_ci#include <linux/async.h> 1862306a36Sopenharmony_ci#include <linux/export.h> 1962306a36Sopenharmony_ci#include <linux/mutex.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <asm/pgalloc.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "sclp.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define SD_EQ_STORE_DATA 0 2662306a36Sopenharmony_ci#define SD_EQ_HALT 1 2762306a36Sopenharmony_ci#define SD_EQ_SIZE 2 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define SD_DI_CONFIG 3 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct sclp_sd_evbuf { 3262306a36Sopenharmony_ci struct evbuf_header hdr; 3362306a36Sopenharmony_ci u8 eq; 3462306a36Sopenharmony_ci u8 di; 3562306a36Sopenharmony_ci u8 rflags; 3662306a36Sopenharmony_ci u64 :56; 3762306a36Sopenharmony_ci u32 id; 3862306a36Sopenharmony_ci u16 :16; 3962306a36Sopenharmony_ci u8 fmt; 4062306a36Sopenharmony_ci u8 status; 4162306a36Sopenharmony_ci u64 sat; 4262306a36Sopenharmony_ci u64 sa; 4362306a36Sopenharmony_ci u32 esize; 4462306a36Sopenharmony_ci u32 dsize; 4562306a36Sopenharmony_ci} __packed; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct sclp_sd_sccb { 4862306a36Sopenharmony_ci struct sccb_header hdr; 4962306a36Sopenharmony_ci struct sclp_sd_evbuf evbuf; 5062306a36Sopenharmony_ci} __packed __aligned(PAGE_SIZE); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/** 5362306a36Sopenharmony_ci * struct sclp_sd_data - Result of a Store Data request 5462306a36Sopenharmony_ci * @esize_bytes: Resulting esize in bytes 5562306a36Sopenharmony_ci * @dsize_bytes: Resulting dsize in bytes 5662306a36Sopenharmony_ci * @data: Pointer to data - must be released using vfree() 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_cistruct sclp_sd_data { 5962306a36Sopenharmony_ci size_t esize_bytes; 6062306a36Sopenharmony_ci size_t dsize_bytes; 6162306a36Sopenharmony_ci void *data; 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/** 6562306a36Sopenharmony_ci * struct sclp_sd_listener - Listener for asynchronous Store Data response 6662306a36Sopenharmony_ci * @list: For enqueueing this struct 6762306a36Sopenharmony_ci * @id: Event ID of response to listen for 6862306a36Sopenharmony_ci * @completion: Can be used to wait for response 6962306a36Sopenharmony_ci * @evbuf: Contains the resulting Store Data response after completion 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_cistruct sclp_sd_listener { 7262306a36Sopenharmony_ci struct list_head list; 7362306a36Sopenharmony_ci u32 id; 7462306a36Sopenharmony_ci struct completion completion; 7562306a36Sopenharmony_ci struct sclp_sd_evbuf evbuf; 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/** 7962306a36Sopenharmony_ci * struct sclp_sd_file - Sysfs representation of a Store Data entity 8062306a36Sopenharmony_ci * @kobj: Kobject 8162306a36Sopenharmony_ci * @data_attr: Attribute for accessing data contents 8262306a36Sopenharmony_ci * @data_mutex: Mutex to serialize access and updates to @data 8362306a36Sopenharmony_ci * @data: Data associated with this entity 8462306a36Sopenharmony_ci * @di: DI value associated with this entity 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_cistruct sclp_sd_file { 8762306a36Sopenharmony_ci struct kobject kobj; 8862306a36Sopenharmony_ci struct bin_attribute data_attr; 8962306a36Sopenharmony_ci struct mutex data_mutex; 9062306a36Sopenharmony_ci struct sclp_sd_data data; 9162306a36Sopenharmony_ci u8 di; 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci#define to_sd_file(x) container_of(x, struct sclp_sd_file, kobj) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic struct kset *sclp_sd_kset; 9662306a36Sopenharmony_cistatic struct sclp_sd_file *config_file; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic LIST_HEAD(sclp_sd_queue); 9962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(sclp_sd_queue_lock); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/** 10262306a36Sopenharmony_ci * sclp_sd_listener_add() - Add listener for Store Data responses 10362306a36Sopenharmony_ci * @listener: Listener to add 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cistatic void sclp_sd_listener_add(struct sclp_sd_listener *listener) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci spin_lock_irq(&sclp_sd_queue_lock); 10862306a36Sopenharmony_ci list_add_tail(&listener->list, &sclp_sd_queue); 10962306a36Sopenharmony_ci spin_unlock_irq(&sclp_sd_queue_lock); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/** 11362306a36Sopenharmony_ci * sclp_sd_listener_remove() - Remove listener for Store Data responses 11462306a36Sopenharmony_ci * @listener: Listener to remove 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_cistatic void sclp_sd_listener_remove(struct sclp_sd_listener *listener) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci spin_lock_irq(&sclp_sd_queue_lock); 11962306a36Sopenharmony_ci list_del(&listener->list); 12062306a36Sopenharmony_ci spin_unlock_irq(&sclp_sd_queue_lock); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/** 12462306a36Sopenharmony_ci * sclp_sd_listener_init() - Initialize a Store Data response listener 12562306a36Sopenharmony_ci * @listener: Response listener to initialize 12662306a36Sopenharmony_ci * @id: Event ID to listen for 12762306a36Sopenharmony_ci * 12862306a36Sopenharmony_ci * Initialize a listener for asynchronous Store Data responses. This listener 12962306a36Sopenharmony_ci * can afterwards be used to wait for a specific response and to retrieve 13062306a36Sopenharmony_ci * the associated response data. 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_cistatic void sclp_sd_listener_init(struct sclp_sd_listener *listener, u32 id) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci memset(listener, 0, sizeof(*listener)); 13562306a36Sopenharmony_ci listener->id = id; 13662306a36Sopenharmony_ci init_completion(&listener->completion); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/** 14062306a36Sopenharmony_ci * sclp_sd_receiver() - Receiver for Store Data events 14162306a36Sopenharmony_ci * @evbuf_hdr: Header of received events 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci * Process Store Data events and complete listeners with matching event IDs. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_cistatic void sclp_sd_receiver(struct evbuf_header *evbuf_hdr) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct sclp_sd_evbuf *evbuf = (struct sclp_sd_evbuf *) evbuf_hdr; 14862306a36Sopenharmony_ci struct sclp_sd_listener *listener; 14962306a36Sopenharmony_ci int found = 0; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci pr_debug("received event (id=0x%08x)\n", evbuf->id); 15262306a36Sopenharmony_ci spin_lock(&sclp_sd_queue_lock); 15362306a36Sopenharmony_ci list_for_each_entry(listener, &sclp_sd_queue, list) { 15462306a36Sopenharmony_ci if (listener->id != evbuf->id) 15562306a36Sopenharmony_ci continue; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci listener->evbuf = *evbuf; 15862306a36Sopenharmony_ci complete(&listener->completion); 15962306a36Sopenharmony_ci found = 1; 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci spin_unlock(&sclp_sd_queue_lock); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (!found) 16562306a36Sopenharmony_ci pr_debug("unsolicited event (id=0x%08x)\n", evbuf->id); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic struct sclp_register sclp_sd_register = { 16962306a36Sopenharmony_ci .send_mask = EVTYP_STORE_DATA_MASK, 17062306a36Sopenharmony_ci .receive_mask = EVTYP_STORE_DATA_MASK, 17162306a36Sopenharmony_ci .receiver_fn = sclp_sd_receiver, 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/** 17562306a36Sopenharmony_ci * sclp_sd_sync() - Perform Store Data request synchronously 17662306a36Sopenharmony_ci * @page: Address of work page - must be below 2GB 17762306a36Sopenharmony_ci * @eq: Input EQ value 17862306a36Sopenharmony_ci * @di: Input DI value 17962306a36Sopenharmony_ci * @sat: Input SAT value 18062306a36Sopenharmony_ci * @sa: Input SA value used to specify the address of the target buffer 18162306a36Sopenharmony_ci * @dsize_ptr: Optional pointer to input and output DSIZE value 18262306a36Sopenharmony_ci * @esize_ptr: Optional pointer to output ESIZE value 18362306a36Sopenharmony_ci * 18462306a36Sopenharmony_ci * Perform Store Data request with specified parameters and wait for completion. 18562306a36Sopenharmony_ci * 18662306a36Sopenharmony_ci * Return %0 on success and store resulting DSIZE and ESIZE values in 18762306a36Sopenharmony_ci * @dsize_ptr and @esize_ptr (if provided). Return non-zero on error. 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_cistatic int sclp_sd_sync(unsigned long page, u8 eq, u8 di, u64 sat, u64 sa, 19062306a36Sopenharmony_ci u32 *dsize_ptr, u32 *esize_ptr) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct sclp_sd_sccb *sccb = (void *) page; 19362306a36Sopenharmony_ci struct sclp_sd_listener listener; 19462306a36Sopenharmony_ci struct sclp_sd_evbuf *evbuf; 19562306a36Sopenharmony_ci int rc; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci sclp_sd_listener_init(&listener, __pa(sccb)); 19862306a36Sopenharmony_ci sclp_sd_listener_add(&listener); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* Prepare SCCB */ 20162306a36Sopenharmony_ci memset(sccb, 0, PAGE_SIZE); 20262306a36Sopenharmony_ci sccb->hdr.length = sizeof(sccb->hdr) + sizeof(sccb->evbuf); 20362306a36Sopenharmony_ci evbuf = &sccb->evbuf; 20462306a36Sopenharmony_ci evbuf->hdr.length = sizeof(*evbuf); 20562306a36Sopenharmony_ci evbuf->hdr.type = EVTYP_STORE_DATA; 20662306a36Sopenharmony_ci evbuf->eq = eq; 20762306a36Sopenharmony_ci evbuf->di = di; 20862306a36Sopenharmony_ci evbuf->id = listener.id; 20962306a36Sopenharmony_ci evbuf->fmt = 1; 21062306a36Sopenharmony_ci evbuf->sat = sat; 21162306a36Sopenharmony_ci evbuf->sa = sa; 21262306a36Sopenharmony_ci if (dsize_ptr) 21362306a36Sopenharmony_ci evbuf->dsize = *dsize_ptr; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Perform command */ 21662306a36Sopenharmony_ci pr_debug("request (eq=%d, di=%d, id=0x%08x)\n", eq, di, listener.id); 21762306a36Sopenharmony_ci rc = sclp_sync_request(SCLP_CMDW_WRITE_EVENT_DATA, sccb); 21862306a36Sopenharmony_ci pr_debug("request done (rc=%d)\n", rc); 21962306a36Sopenharmony_ci if (rc) 22062306a36Sopenharmony_ci goto out; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* Evaluate response */ 22362306a36Sopenharmony_ci if (sccb->hdr.response_code == 0x73f0) { 22462306a36Sopenharmony_ci pr_debug("event not supported\n"); 22562306a36Sopenharmony_ci rc = -EIO; 22662306a36Sopenharmony_ci goto out_remove; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci if (sccb->hdr.response_code != 0x0020 || !(evbuf->hdr.flags & 0x80)) { 22962306a36Sopenharmony_ci rc = -EIO; 23062306a36Sopenharmony_ci goto out; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci if (!(evbuf->rflags & 0x80)) { 23362306a36Sopenharmony_ci rc = wait_for_completion_interruptible(&listener.completion); 23462306a36Sopenharmony_ci if (rc) 23562306a36Sopenharmony_ci goto out; 23662306a36Sopenharmony_ci evbuf = &listener.evbuf; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci switch (evbuf->status) { 23962306a36Sopenharmony_ci case 0: 24062306a36Sopenharmony_ci if (dsize_ptr) 24162306a36Sopenharmony_ci *dsize_ptr = evbuf->dsize; 24262306a36Sopenharmony_ci if (esize_ptr) 24362306a36Sopenharmony_ci *esize_ptr = evbuf->esize; 24462306a36Sopenharmony_ci pr_debug("success (dsize=%u, esize=%u)\n", evbuf->dsize, 24562306a36Sopenharmony_ci evbuf->esize); 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci case 3: 24862306a36Sopenharmony_ci rc = -ENOENT; 24962306a36Sopenharmony_ci break; 25062306a36Sopenharmony_ci default: 25162306a36Sopenharmony_ci rc = -EIO; 25262306a36Sopenharmony_ci break; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ciout: 25762306a36Sopenharmony_ci if (rc && rc != -ENOENT) { 25862306a36Sopenharmony_ci /* Provide some information about what went wrong */ 25962306a36Sopenharmony_ci pr_warn("Store Data request failed (eq=%d, di=%d, " 26062306a36Sopenharmony_ci "response=0x%04x, flags=0x%02x, status=%d, rc=%d)\n", 26162306a36Sopenharmony_ci eq, di, sccb->hdr.response_code, evbuf->hdr.flags, 26262306a36Sopenharmony_ci evbuf->status, rc); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ciout_remove: 26662306a36Sopenharmony_ci sclp_sd_listener_remove(&listener); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return rc; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/** 27262306a36Sopenharmony_ci * sclp_sd_store_data() - Obtain data for specified Store Data entity 27362306a36Sopenharmony_ci * @result: Resulting data 27462306a36Sopenharmony_ci * @di: DI value associated with this entity 27562306a36Sopenharmony_ci * 27662306a36Sopenharmony_ci * Perform a series of Store Data requests to obtain the size and contents of 27762306a36Sopenharmony_ci * the specified Store Data entity. 27862306a36Sopenharmony_ci * 27962306a36Sopenharmony_ci * Return: 28062306a36Sopenharmony_ci * %0: Success - result is stored in @result. @result->data must be 28162306a36Sopenharmony_ci * released using vfree() after use. 28262306a36Sopenharmony_ci * %-ENOENT: No data available for this entity 28362306a36Sopenharmony_ci * %<0: Other error 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_cistatic int sclp_sd_store_data(struct sclp_sd_data *result, u8 di) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci u32 dsize = 0, esize = 0; 28862306a36Sopenharmony_ci unsigned long page, asce = 0; 28962306a36Sopenharmony_ci void *data = NULL; 29062306a36Sopenharmony_ci int rc; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci page = __get_free_page(GFP_KERNEL | GFP_DMA); 29362306a36Sopenharmony_ci if (!page) 29462306a36Sopenharmony_ci return -ENOMEM; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* Get size */ 29762306a36Sopenharmony_ci rc = sclp_sd_sync(page, SD_EQ_SIZE, di, 0, 0, &dsize, &esize); 29862306a36Sopenharmony_ci if (rc) 29962306a36Sopenharmony_ci goto out; 30062306a36Sopenharmony_ci if (dsize == 0) 30162306a36Sopenharmony_ci goto out_result; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* Allocate memory */ 30462306a36Sopenharmony_ci data = vzalloc(array_size((size_t)dsize, PAGE_SIZE)); 30562306a36Sopenharmony_ci if (!data) { 30662306a36Sopenharmony_ci rc = -ENOMEM; 30762306a36Sopenharmony_ci goto out; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* Get translation table for buffer */ 31162306a36Sopenharmony_ci asce = base_asce_alloc((unsigned long) data, dsize); 31262306a36Sopenharmony_ci if (!asce) { 31362306a36Sopenharmony_ci vfree(data); 31462306a36Sopenharmony_ci rc = -ENOMEM; 31562306a36Sopenharmony_ci goto out; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* Get data */ 31962306a36Sopenharmony_ci rc = sclp_sd_sync(page, SD_EQ_STORE_DATA, di, asce, (u64) data, &dsize, 32062306a36Sopenharmony_ci &esize); 32162306a36Sopenharmony_ci if (rc) { 32262306a36Sopenharmony_ci /* Cancel running request if interrupted */ 32362306a36Sopenharmony_ci if (rc == -ERESTARTSYS) 32462306a36Sopenharmony_ci sclp_sd_sync(page, SD_EQ_HALT, di, 0, 0, NULL, NULL); 32562306a36Sopenharmony_ci vfree(data); 32662306a36Sopenharmony_ci goto out; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ciout_result: 33062306a36Sopenharmony_ci result->esize_bytes = (size_t) esize * PAGE_SIZE; 33162306a36Sopenharmony_ci result->dsize_bytes = (size_t) dsize * PAGE_SIZE; 33262306a36Sopenharmony_ci result->data = data; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ciout: 33562306a36Sopenharmony_ci base_asce_free(asce); 33662306a36Sopenharmony_ci free_page(page); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return rc; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci/** 34262306a36Sopenharmony_ci * sclp_sd_data_reset() - Reset Store Data result buffer 34362306a36Sopenharmony_ci * @data: Data buffer to reset 34462306a36Sopenharmony_ci * 34562306a36Sopenharmony_ci * Reset @data to initial state and release associated memory. 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_cistatic void sclp_sd_data_reset(struct sclp_sd_data *data) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci vfree(data->data); 35062306a36Sopenharmony_ci data->data = NULL; 35162306a36Sopenharmony_ci data->dsize_bytes = 0; 35262306a36Sopenharmony_ci data->esize_bytes = 0; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci/** 35662306a36Sopenharmony_ci * sclp_sd_file_release() - Release function for sclp_sd_file object 35762306a36Sopenharmony_ci * @kobj: Kobject embedded in sclp_sd_file object 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_cistatic void sclp_sd_file_release(struct kobject *kobj) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct sclp_sd_file *sd_file = to_sd_file(kobj); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci sclp_sd_data_reset(&sd_file->data); 36462306a36Sopenharmony_ci kfree(sd_file); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci/** 36862306a36Sopenharmony_ci * sclp_sd_file_update() - Update contents of sclp_sd_file object 36962306a36Sopenharmony_ci * @sd_file: Object to update 37062306a36Sopenharmony_ci * 37162306a36Sopenharmony_ci * Obtain the current version of data associated with the Store Data entity 37262306a36Sopenharmony_ci * @sd_file. 37362306a36Sopenharmony_ci * 37462306a36Sopenharmony_ci * On success, return %0 and generate a KOBJ_CHANGE event to indicate that the 37562306a36Sopenharmony_ci * data may have changed. Return non-zero otherwise. 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_cistatic int sclp_sd_file_update(struct sclp_sd_file *sd_file) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci const char *name = kobject_name(&sd_file->kobj); 38062306a36Sopenharmony_ci struct sclp_sd_data data; 38162306a36Sopenharmony_ci int rc; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci rc = sclp_sd_store_data(&data, sd_file->di); 38462306a36Sopenharmony_ci if (rc) { 38562306a36Sopenharmony_ci if (rc == -ENOENT) { 38662306a36Sopenharmony_ci pr_info("No data is available for the %s data entity\n", 38762306a36Sopenharmony_ci name); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci return rc; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci mutex_lock(&sd_file->data_mutex); 39362306a36Sopenharmony_ci sclp_sd_data_reset(&sd_file->data); 39462306a36Sopenharmony_ci sd_file->data = data; 39562306a36Sopenharmony_ci mutex_unlock(&sd_file->data_mutex); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci pr_info("A %zu-byte %s data entity was retrieved\n", data.dsize_bytes, 39862306a36Sopenharmony_ci name); 39962306a36Sopenharmony_ci kobject_uevent(&sd_file->kobj, KOBJ_CHANGE); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci return 0; 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci/** 40562306a36Sopenharmony_ci * sclp_sd_file_update_async() - Wrapper for asynchronous update call 40662306a36Sopenharmony_ci * @data: Object to update 40762306a36Sopenharmony_ci * @cookie: Unused 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_cistatic void sclp_sd_file_update_async(void *data, async_cookie_t cookie) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct sclp_sd_file *sd_file = data; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci sclp_sd_file_update(sd_file); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci/** 41762306a36Sopenharmony_ci * reload_store() - Store function for "reload" sysfs attribute 41862306a36Sopenharmony_ci * @kobj: Kobject of sclp_sd_file object 41962306a36Sopenharmony_ci * @attr: Reload attribute 42062306a36Sopenharmony_ci * @buf: Data written to sysfs attribute 42162306a36Sopenharmony_ci * @count: Count of bytes written 42262306a36Sopenharmony_ci * 42362306a36Sopenharmony_ci * Initiate a reload of the data associated with an sclp_sd_file object. 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_cistatic ssize_t reload_store(struct kobject *kobj, struct kobj_attribute *attr, 42662306a36Sopenharmony_ci const char *buf, size_t count) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct sclp_sd_file *sd_file = to_sd_file(kobj); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci sclp_sd_file_update(sd_file); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return count; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic struct kobj_attribute reload_attr = __ATTR_WO(reload); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic struct attribute *sclp_sd_file_default_attrs[] = { 43862306a36Sopenharmony_ci &reload_attr.attr, 43962306a36Sopenharmony_ci NULL, 44062306a36Sopenharmony_ci}; 44162306a36Sopenharmony_ciATTRIBUTE_GROUPS(sclp_sd_file_default); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic struct kobj_type sclp_sd_file_ktype = { 44462306a36Sopenharmony_ci .sysfs_ops = &kobj_sysfs_ops, 44562306a36Sopenharmony_ci .release = sclp_sd_file_release, 44662306a36Sopenharmony_ci .default_groups = sclp_sd_file_default_groups, 44762306a36Sopenharmony_ci}; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/** 45062306a36Sopenharmony_ci * data_read() - Read function for "data" sysfs attribute 45162306a36Sopenharmony_ci * @file: Open file pointer 45262306a36Sopenharmony_ci * @kobj: Kobject of sclp_sd_file object 45362306a36Sopenharmony_ci * @attr: Data attribute 45462306a36Sopenharmony_ci * @buffer: Target buffer 45562306a36Sopenharmony_ci * @off: Requested file offset 45662306a36Sopenharmony_ci * @size: Requested number of bytes 45762306a36Sopenharmony_ci * 45862306a36Sopenharmony_ci * Store the requested portion of the Store Data entity contents into the 45962306a36Sopenharmony_ci * specified buffer. Return the number of bytes stored on success, or %0 46062306a36Sopenharmony_ci * on EOF. 46162306a36Sopenharmony_ci */ 46262306a36Sopenharmony_cistatic ssize_t data_read(struct file *file, struct kobject *kobj, 46362306a36Sopenharmony_ci struct bin_attribute *attr, char *buffer, 46462306a36Sopenharmony_ci loff_t off, size_t size) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci struct sclp_sd_file *sd_file = to_sd_file(kobj); 46762306a36Sopenharmony_ci size_t data_size; 46862306a36Sopenharmony_ci char *data; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci mutex_lock(&sd_file->data_mutex); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci data = sd_file->data.data; 47362306a36Sopenharmony_ci data_size = sd_file->data.dsize_bytes; 47462306a36Sopenharmony_ci if (!data || off >= data_size) { 47562306a36Sopenharmony_ci size = 0; 47662306a36Sopenharmony_ci } else { 47762306a36Sopenharmony_ci if (off + size > data_size) 47862306a36Sopenharmony_ci size = data_size - off; 47962306a36Sopenharmony_ci memcpy(buffer, data + off, size); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci mutex_unlock(&sd_file->data_mutex); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci return size; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci/** 48862306a36Sopenharmony_ci * sclp_sd_file_create() - Add a sysfs file representing a Store Data entity 48962306a36Sopenharmony_ci * @name: Name of file 49062306a36Sopenharmony_ci * @di: DI value associated with this entity 49162306a36Sopenharmony_ci * 49262306a36Sopenharmony_ci * Create a sysfs directory with the given @name located under 49362306a36Sopenharmony_ci * 49462306a36Sopenharmony_ci * /sys/firmware/sclp_sd/ 49562306a36Sopenharmony_ci * 49662306a36Sopenharmony_ci * The files in this directory can be used to access the contents of the Store 49762306a36Sopenharmony_ci * Data entity associated with @DI. 49862306a36Sopenharmony_ci * 49962306a36Sopenharmony_ci * Return pointer to resulting sclp_sd_file object on success, %NULL otherwise. 50062306a36Sopenharmony_ci * The object must be freed by calling kobject_put() on the embedded kobject 50162306a36Sopenharmony_ci * pointer after use. 50262306a36Sopenharmony_ci */ 50362306a36Sopenharmony_cistatic __init struct sclp_sd_file *sclp_sd_file_create(const char *name, u8 di) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct sclp_sd_file *sd_file; 50662306a36Sopenharmony_ci int rc; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci sd_file = kzalloc(sizeof(*sd_file), GFP_KERNEL); 50962306a36Sopenharmony_ci if (!sd_file) 51062306a36Sopenharmony_ci return NULL; 51162306a36Sopenharmony_ci sd_file->di = di; 51262306a36Sopenharmony_ci mutex_init(&sd_file->data_mutex); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* Create kobject located under /sys/firmware/sclp_sd/ */ 51562306a36Sopenharmony_ci sd_file->kobj.kset = sclp_sd_kset; 51662306a36Sopenharmony_ci rc = kobject_init_and_add(&sd_file->kobj, &sclp_sd_file_ktype, NULL, 51762306a36Sopenharmony_ci "%s", name); 51862306a36Sopenharmony_ci if (rc) { 51962306a36Sopenharmony_ci kobject_put(&sd_file->kobj); 52062306a36Sopenharmony_ci return NULL; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci sysfs_bin_attr_init(&sd_file->data_attr); 52462306a36Sopenharmony_ci sd_file->data_attr.attr.name = "data"; 52562306a36Sopenharmony_ci sd_file->data_attr.attr.mode = 0444; 52662306a36Sopenharmony_ci sd_file->data_attr.read = data_read; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci rc = sysfs_create_bin_file(&sd_file->kobj, &sd_file->data_attr); 52962306a36Sopenharmony_ci if (rc) { 53062306a36Sopenharmony_ci kobject_put(&sd_file->kobj); 53162306a36Sopenharmony_ci return NULL; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* 53562306a36Sopenharmony_ci * For completeness only - users interested in entity data should listen 53662306a36Sopenharmony_ci * for KOBJ_CHANGE instead. 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_ci kobject_uevent(&sd_file->kobj, KOBJ_ADD); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* Don't let a slow Store Data request delay further initialization */ 54162306a36Sopenharmony_ci async_schedule(sclp_sd_file_update_async, sd_file); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci return sd_file; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci/** 54762306a36Sopenharmony_ci * sclp_sd_init() - Initialize sclp_sd support and register sysfs files 54862306a36Sopenharmony_ci */ 54962306a36Sopenharmony_cistatic __init int sclp_sd_init(void) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci int rc; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci rc = sclp_register(&sclp_sd_register); 55462306a36Sopenharmony_ci if (rc) 55562306a36Sopenharmony_ci return rc; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* Create kset named "sclp_sd" located under /sys/firmware/ */ 55862306a36Sopenharmony_ci rc = -ENOMEM; 55962306a36Sopenharmony_ci sclp_sd_kset = kset_create_and_add("sclp_sd", NULL, firmware_kobj); 56062306a36Sopenharmony_ci if (!sclp_sd_kset) 56162306a36Sopenharmony_ci goto err_kset; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci rc = -EINVAL; 56462306a36Sopenharmony_ci config_file = sclp_sd_file_create("config", SD_DI_CONFIG); 56562306a36Sopenharmony_ci if (!config_file) 56662306a36Sopenharmony_ci goto err_config; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci return 0; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cierr_config: 57162306a36Sopenharmony_ci kset_unregister(sclp_sd_kset); 57262306a36Sopenharmony_cierr_kset: 57362306a36Sopenharmony_ci sclp_unregister(&sclp_sd_register); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci return rc; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_cidevice_initcall(sclp_sd_init); 578